Warnings
A malformed or incomplete recipe still compiles — the compiler and analyzer collect structured Warning objects instead of throwing, so callers can render a recipe and surface what’s wrong at the same time. WarningCode and friends are exported from @gram-lang/kitchen.
The Warning interface
Section titled “The Warning interface”compile() returns them on CompilationResult.warnings; analyze() carries those through and may push additional ones onto the same array on AnalyzedCompilationResult.warnings. Never a bare string — .message is always present.
Severity & --strict
Section titled “Severity & --strict”Structural integrity issues — a reference to something that doesn’t exist, a naming collision, or an unresolvable module — are error. Recoverable gaps (estimation gaps, incomplete annotations) are warning, and contextual notifications (surplus quantities, batch scaling, resource contention) are info. This is exactly the distinction the CLI’s gram check --strict flag uses: without --strict, only error-severity codes fail the command; with it, every warning and info is promoted to error too. Build your own strict-mode logic on warningSeverity[code] the same way.
| Code | Severity | Message template |
|---|---|---|
VARIABLE_NOT_FOUND | warning | Cannot resolve relative quantity: target intermediate '&{targetName}' is not defined. |
RELATIVE_QUANTITY_UNRESOLVED | warning | Cannot resolve relative quantity: target ingredient '@{targetName}' was not found in the current section. |
RELATIVE_QUANTITY_UNKNOWN_MASS | warning | Cannot compute relative quantity for '{item}': mass of target '{targetName}' is unknown. |
CIRCULAR_REFERENCE | error | Circular reference detected: '{name}' depends on itself. |
UNDEFINED_REFERENCE | error | Undefined reference '{prefix}{name}' — no prior step or section produces this item. |
MISSING_UNIT | warning | {type} requires an explicit unit (e.g. min, s, °C). |
INVALID_UNIT | warning | Invalid unit "{value}" for {type}. |
SCOPE_CONFLICT | error | Intermediate variable '&{varName}' is redefined; variable names must be unique across the recipe. |
MISSING_INGREDIENT | warning | Ingredient "{id}" not found in database — nutritional metrics and density conversions unavailable. |
MISSING_MACROS | info | Ingredient "{id}" has no macronutrient data in database — nutritional totals are partial. |
UNKNOWN_MASS | info | Cannot calculate mass for "{id}" — omitted from nutritional totals. |
INVALID_MODIFIER_COMBINATION | warning | Incompatible modifiers on "{item}": {combination}. |
COMPOSITE_PARENT_CONFLICT | error | Composite child "{childName}" was already linked to parent "{previousParent}" — using it with a different parent "{newParent}" here means both will share the same database entry, which is very likely wrong. |
INVALID_BAKERS_REFERENCE | warning | '{item}' cannot be used as the Baker's percentage reference (*). |
NO_BAKERS_REFERENCE | warning | Baker's percentages (%) are used but no base flour (*) was designated. |
TIME_PARADOX | warning | Timeline conflict: {cause} is pulled earlier than recipe start to satisfy {conflict}. |
TRACK_CONTENTION | info | Resource contention on track '{trackName}': delayed by {delay} min for '{item}'. |
MODULE_NOT_FOUND | error | Module "{specifier}" could not be found or resolved. |
MODULE_PARSE_ERROR | error | Syntax error in imported module "{specifier}": {parseMessage} |
MODULE_CYCLE | error | Circular module import detected: {chain}. |
MODULE_DEPTH_EXCEEDED | error | Import depth limit exceeded ({depth}) while importing "{specifier}". |
MODULE_EXPORT_NOT_FOUND | error | Module "{specifier}" does not export '&{exported}' — it exists in the module but isn't re-exported. Add '-> &{exported}' to the section that produces it. |
UNUSED_IMPORT | warning | Unused import '&{local}' from "{specifier}". |
UNRESOLVED_MODULE_YIELD | error | Cannot compute yield for '&{binding}' from "{specifier}": missing physical mass data for one or more ingredients. |
ESTIMATED_MODULE_YIELD | warning | Yield of '&{binding}' from "{specifier}" is estimated using standard ingredient densities or unit weights. Scale factor is approximate. |
MODULE_UNIT_MISMATCH | error | Unit mismatch for '&{binding}' from "{specifier}": requested in '{requestedUnit}' but yields in '{yieldUnit}' without a conversion density. |
MODULE_BATCH_INTERPRETATION | info | '&{binding}' from "{specifier}" requested without unit — scaled as {batches} batch(es) of the module. |
IMPORTED_BAKERS_REFERENCE_DROPPED | info | Baker's percentage base (*) from "{specifier}" is scoped to its own module and was not imported. |
DENSITY_OVERRIDE_SHADOWED | info | Density for "{ingredient}" in host recipe ({hostValue}) overrides module "{specifier}" ({moduleValue}). |
MODULE_SURPLUS | info | Scaling "{specifier}" for '&{binding}' yields a surplus: {surplus}. |
MODULE_SPECIFIER_INVALID | error | Invalid module path "{specifier}": {reason} |
MODULE_SCHEME_UNSUPPORTED | error | Unsupported URL scheme in module specifier: "{specifier}". |
STOCKED_RETRO_PLANNING_IGNORED | warning | Stocked module "{specifier}" has a retro-planning offset "~{...}", which is ignored because stocked items require no prep time. |
RETRO_PLANNING_OVERRIDE_SHADOWED | info | Host retro-planning offset on "@use {specifier}" overrides the module's internal offset. |
MODULE_BINDING_SHADOWS_INGREDIENT | warning | Imported binding '&{binding}' from "{specifier}" shares name with a database ingredient. |
STOCKED_DESTRUCTURED_NUTRITION_BLENDED | info | Stocked module "{specifier}" uses destructured imports — nutrition profile is averaged across the entire module. |
VARIABLE_NOT_FOUND
Section titled “VARIABLE_NOT_FOUND”A relative quantity references an intermediate variable (50% of &name) that hasn’t been declared as an intermediate output (>> name) anywhere in the recipe. Fix: declare the variable before referencing it, or check for a typo in the name.
RELATIVE_QUANTITY_UNRESOLVED
Section titled “RELATIVE_QUANTITY_UNRESOLVED”A relative quantity references an ingredient (50% of @name) that hasn’t appeared earlier in the same section — relative-to-ingredient targets are section-scoped, unlike variables. Fix: move the referenced ingredient earlier in the same section, or reference a variable (&name) instead if it’s meant to be recipe-wide.
RELATIVE_QUANTITY_UNKNOWN_MASS
Section titled “RELATIVE_QUANTITY_UNKNOWN_MASS”Pushed during analysis: the target of a relative quantity was found, but its own mass couldn’t be computed (no resolvable unit/density), so the percentage can’t be applied. Fix: give the target ingredient a standardizable unit, or a density/unit_weight entry in the ingredient database.
CIRCULAR_REFERENCE
Section titled “CIRCULAR_REFERENCE”An ingredient’s relative quantity targets itself (@flour{50% of @flour}). Fix: remove the self-reference — a percentage-based quantity must target a different ingredient or variable.
UNDEFINED_REFERENCE
Section titled “UNDEFINED_REFERENCE”A bare reference (&name) or a referenceable ingredient (@&name) points to something that was never registered earlier in the recipe. Fix: introduce the ingredient (without &) before referencing it, or check for a typo.
MISSING_UNIT
Section titled “MISSING_UNIT”A Timer or Temperature was written without an explicit unit (e.g. ~{10} instead of ~{10 min}). Fix: add an explicit unit.
INVALID_UNIT
Section titled “INVALID_UNIT”Either a Timer was given a non-numeric (text) quantity, or a Temperature was given a unit other than Celsius/Fahrenheit. Fix: use a numeric value + a recognized unit for timers; use °C or °F for temperatures.
SCOPE_CONFLICT
Section titled “SCOPE_CONFLICT”Two sections declare the same intermediate/global variable name (>> name). Variable names must be unique across the whole recipe, not just within a section. Fix: rename one of the two declarations.
MISSING_INGREDIENT
Section titled “MISSING_INGREDIENT”During nutrition estimation, an ingredient with a computable mass has no matching entry (by id or alias) in the ingredient database at all. Fix: add the ingredient (or an alias to an existing entry) to your database.
MISSING_MACROS
Section titled “MISSING_MACROS”The ingredient exists in the database, but its entry has no nutrition block. Fix: add a nutrition block to that database entry.
UNKNOWN_MASS
Section titled “UNKNOWN_MASS”Nutrition estimation couldn’t compute a mass for this ingredient at all (unresolvable unit, no density/unit_weight), so it’s excluded from the totals. Fix: same as RELATIVE_QUANTITY_UNKNOWN_MASS — give it a standardizable unit or physical data in the database.
INVALID_MODIFIER_COMBINATION
Section titled “INVALID_MODIFIER_COMBINATION”Conflicting or duplicated modifiers on the same ingredient/cookware — e.g. optional (?) with important (*), hidden (-) with important (*), hidden (-) with referenceable (&), or the same modifier twice. The specific combination is named in .message. Fix: remove the conflicting modifier.
INVALID_BAKERS_REFERENCE
Section titled “INVALID_BAKERS_REFERENCE”The ingredient marked as the baker’s-percentage base (via the * modifier or the bakersReference option) has a mass that was itself derived from another ingredient’s relative quantity — it can’t also serve as the 100% anchor, since that would be circular. Fix: mark a different ingredient with an absolute (non-relative) quantity as the reference.
NO_BAKERS_REFERENCE
Section titled “NO_BAKERS_REFERENCE”Baker’s math was explicitly requested (enableBakersMath with a bare * search, or an explicit bakersReference id) but no ingredient matched. Fix: mark an ingredient with the * modifier, or correct the bakersReference id to match an existing ingredient.
COMPOSITE_PARENT_CONFLICT
Section titled “COMPOSITE_PARENT_CONFLICT”A short composite child name (e.g. @juice) is drawn from two different parent ingredients within the same recipe (e.g. <@lemon in one step and <@orange in another). Fix: use the full name for the child (e.g. @lemon juice and @orange juice) to prevent database identity collisions.
MODULE_NOT_FOUND
Section titled “MODULE_NOT_FOUND”An imported module specified in a @use directive could not be resolved or found on the filesystem. Fix: verify the module path, file extension (.gram), or configured path aliases in config.yaml.