Error codes
Nola diagnostics use stable codes:
NOLA1xxx: parse errors, reported by the editor,nola checkandnola build.NOLA2xxx: compile errors, reported by the same tools.NOLA3xxx: runtime errors from the runtime, loader or a provider.NOLA4xxx: build-time errors from bundler plugins.
Each code has a permanent link, such as /docs/reference/error-codes/#nola2001. Codes marked not raised are reserved or retired and will not be reused.
Parse errors (NOLA1xxx)
NOLA1001
Section titled “NOLA1001”(the parser’s own message)
Cause: a syntax error without a more specific Nola code, such as an unbalanced brace or stray token. Fix: check the location in the message and correct the TypeScript or JavaScript syntax.
NOLA1003
Section titled “NOLA1003”
askis a reserved word in .tsi files and cannot be used as an identifier.
Cause: ask used as a variable, function or parameter name.
Fix: rename it. ask stays legal as a member or property name (obj.ask).
NOLA1004
Section titled “NOLA1004”this Nola construct is reserved for a future Nola version.
Cause: syntax reserved for future use: an infer generator (infer function*), a marker on a class method, a bodiless infer function, or the derive-all call fn(..). See Restrictions for other reserved forms.
Fix: use a top-level infer function declaration and write each call-intent argument as an explicit extractor (..`…`: T).
infer on an arrow or function expression produces NOLA1001 instead. It is a keyword only directly before function at statement or export position.
NOLA1005
Section titled “NOLA1005”expected a template literal prompt after
...
Cause: .. followed by something other than a backtick instruction.
Fix: write ..`instruction`: T.
NOLA1006
Section titled “NOLA1006”Not raised — reserved (infer without a following function is reported as NOLA1001).
NOLA1007
Section titled “NOLA1007”a marker after a plain function’s name is not valid — only an
infer functionhas that slot, and there it is reserved (NOLA1019).
Cause: a backtick literal between a plain function’s name and its parameter list. Fix: drop it; a plain function takes no marker. For an infer function, write the text as a context statement in the body (Context statements).
NOLA1009
Section titled “NOLA1009”expected a model name after
ask with— for a dynamic model use.withModel(...)on the intent.
Cause: a string literal, a number or a parenthesized expression after with — anything that is not a bare identifier. (A dotted name such as provider.fast is parsed as the identifier provider followed by a stray .fast, so it reports NOLA1005/NOLA1001 rather than this code.)
Fix: name the model in nola.config.ts and use that bare identifier; for a dynamic choice use .withModel(…).
// not-checked — WRONGexport infer function summarize(.text: string, useFast: boolean) { const a = ask with "fast" ..`a rough summary`: string; const b = ask with (useFast ? "fast" : "careful") ..`a rough summary`: string; return { a, b };}// summarize.tsi — RIGHTexport infer function summarize(.text: string, useFast: boolean) { const c = ask with fast `a rough summary`: string; const d = ask (..`a rough summary`: string).withModel(useFast ? "fast" : "careful"); return { c, d };}NOLA1010
Section titled “NOLA1010”
.context parameters are only allowed on infer function parameters.
Cause: a .name parameter on a plain function, or a .name binding outside an infer body / the module body — there is no inference context to put the value in.
Fix: drop the dot (it is an ordinary argument or variable), or make the function an infer function.
// not-checked — WRONGfunction summarize(.text: string) { return text.slice(0, 10);}// summarize-right.tsi — RIGHTexport infer function summarize(.text: string) { return ask `a one-sentence summary`: string;}NOLA1011
Section titled “NOLA1011”
.on this parameter form is reserved for a future Nola version — use a plain identifier parameter.
Cause: . on a destructuring pattern or a parameter with a default value.
Fix: use a plain identifier parameter (.issue: Issue) and destructure or default inside the body.
NOLA1012
Section titled “NOLA1012”incomplete
.context parameter — write.name.
Cause: a . with no parameter name after it (usually mid-typing).
Fix: finish the name.
NOLA1013
Section titled “NOLA1013”contextual parameters take one dot — write
.name(..is the extractor sigil).
Cause: ..name on a parameter.
Fix: use one dot: .name. Two dots introduce an extractor.
NOLA1014
Section titled “NOLA1014”
var .xis reserved — a contextual binding isconst .xorlet .x.
Cause: the dot on a var declaration — var hoists to undefined, which no ask should see.
Fix: write const .x or let .x — see Contextual bindings.
NOLA1017
Section titled “NOLA1017”the implied extractor needs whitespace before its template — write
ask `…`(ask`…`reads as a tagged template).
Cause: a template literal glued to ask (or to the model name after ask with) — ask`the user's name`: string. Without the space it reads as a tagged template call on ask, which is not what runs.
Fix: put at least one space (any whitespace) between ask and the backtick, or write the explicit sigil — ask ..`…`: T. See Extractors.
NOLA1018
Section titled “NOLA1018”expected a type after the extractor’s
:on the same line — write`…`: T.
Cause: a colon after an extractor’s closing backtick starts its type, and nothing type-like follows it on that line — ask `the user id`: at the end of a line, or ask `the user id`: ;. The type has to start on the colon’s line because TypeScript’s type grammar reads across line breaks: otherwise an unfinished : above an existing statement would swallow that statement’s first expression as a type name.
Fix: write the type right after the colon — ask `the user id`: number — or use the angle-bracket spelling, ask `the user id`<number>. See Extractors.
NOLA1019
Section titled “NOLA1019”the marker after an infer function’s name is reserved for a future Nola version — write the instruction as a context statement in the body:
….
Cause: a backtick literal between an infer function’s name and its parameter list — infer function f`…`(…). That slot no longer carries the instruction.
Fix: write the text as a context statement in the body (Context statements):
export infer function triage(.ticket: string) { `triage the ticket like a support lead` return ask `the severity: low, medium or high`: "low" | "medium" | "high";}NOLA1020
Section titled “NOLA1020”a value in a context statement is a name, a call or a bracketed expression, followed by text or the end of the statement — wrap anything else in parentheses, or assign it to a local first (
await,askandthiscannot be values).
Cause: after the text of a context statement, a token that cannot continue it on the same line: an operator or an arrow after a value (`total` a + b, `map` x => x), or typeof, void, await, ask, this, .., function or class where a value would start. Also a value that ends with a backtick, which takes the text after it for a tagged template: `x` foo<string> `more`, `x` new Foo `more`.
Fix: parenthesize the expression — `total` (a + b), `x` (foo<string>) `more`, `x` (new Foo) `more` — or put it in a ${} hole of the text. await, ask and this cannot go in a value even in parentheses: a parenthesized ask is NOLA2010, and a parenthesized await or this is a TypeScript error (TS1308, TS2683), because a context statement is read at each ask, outside the async body, in a hoisted function of its own. Compute the value first, assign it to a local and write its name: const user = this.user;, then `Page` user.
// not-checked — WRONGexport infer function total(.items: string[], a: number, b: number) { `total` a + b `items`; // an operator after a value return ask `a summary`: string;}// total.tsi — RIGHTexport infer function total(.items: string[], a: number, b: number) { `total` (a + b) `items`; return ask `a summary`: string;}Compile errors (NOLA2xxx)
NOLA2001
Section titled “NOLA2001”
askis only allowed directly inside an infer function body or the module body.
Cause: ask inside a plain function or a nested closure — even one written inside an infer function — or in a class field initializer / static block, where await is illegal.
Fix: ask directly in the infer body or at the top level of the module; from plain TS, await the infer function instead. Constructing an extractor anywhere is fine — only resolving it is restricted.
// not-checked — WRONGconst g = async () => ask `the kind`: string; // plain closure
export infer function f(.t: string) { const h = () => ask `the kind`: string; // nested closure return h();}// kind.tsi — RIGHTexport infer function f(.t: string) { return ask `the kind`: string;}NOLA2002
Section titled “NOLA2002”unsupported type for intent schema: …
Cause: an extractor’s type resolves to a type the checker-backed derivation cannot turn into a JSON Schema — Map, Set, Promise, RegExp, functions, a generic used without arguments — or a member of it does; the message names the member and the type.
Fix: ask for a JSON-shaped type and convert afterwards in plain TypeScript. See Schema derivation.
// not-checked — WRONGexport infer function tally(.doc: string) { return ask `counts per label`: Map<string, number>;}// tally.tsi — RIGHTexport infer function tally(.doc: string) { const counts = ask `counts per label`: { label: string; count: number }[]; return new Map(counts.map((c) => [c.label, c.count]));}NOLA2003
Section titled “NOLA2003”infer functions must be declared at module top level.
Cause: an infer function declared inside another function or block.
Fix: move it to module level.
NOLA2004
Section titled “NOLA2004”an extractor used as a call-intent argument must have an explicit type — write ..
…: T here.
Cause: a call-intent slot written as ..`…` with no type. (A bare template with no type, createTicket(`a short ticket title`, 2), is a plain string argument, not a slot: the call stays an ordinary call.)
Fix: give every slot a type, `…`: T (or `…`<T>); the .. is implied in a call’s argument list.
// not-checked — WRONGdeclare function createTicket(title: string, priority: number): Promise<string>;export infer function fileTicket(.request: string) { return ask createTicket(..`a short ticket title`, 2);}// file-ticket.tsi — RIGHTdeclare function createTicket(title: string, priority: number): Promise<string>;export infer function fileTicket(.request: string) { return ask createTicket(`a short ticket title`: string, 2);}NOLA2007
Section titled “NOLA2007”“./x.tsi” names neither a Nola file nor a TypeScript module
Cause: a relative .tsi import — written by you in a .ts file, or generated for a type a .tsi imports — found no x.tsi, x.ts or x.d.ts next to the importer.
Fix: check the specifier. ./models.tsi means the Nola file models.tsi when it exists, otherwise the view of models.ts. See TypeScript interop.
NOLA2008
Section titled “NOLA2008”contextual parameter ‘…’ has a type that cannot be derived for inference: …. Set compiler.underivableContextType to “prune” or “omit” in nola.config.ts to allow it.
Cause: a .param whose resolved type is not derivable (same rules as NOLA2002); its value would have to be serialized into the prompt. The message names the offending member.
Fix: pass a JSON-shaped view, keep the exotic value as a plain parameter, or relax compiler.underivableContextType — see Contextual parameters.
// not-checked — WRONGexport infer function topLabel(.index: Map<string, number>) { return ask `the label with the highest count`: string;}// top-label.tsi — RIGHTexport infer function topLabel(.index: { label: string; count: number }[]) { return ask `the label with the highest count`: string;}NOLA2010
Section titled “NOLA2010”Nola constructs are not allowed inside a context statement or an instruction literal’s hole; a hinted call intent used as a value must be parenthesized.
Cause: ..`…` or ask inside a context statement — as a parenthesized value, nested in a bracketed value or in a ${} hole of its text — or inside a ${} hole of a call hint. A context statement is read at each ask with no frame of its own, and a call hint is re-emitted from source. A call intent is a legal value, but a hinted one, fn`hint`(…), written bare is read as the value fn, then the text hint, then a parenthesized group: its ..`…`: T argument is the bare extractor this code reports (written without the dots, (`…`: T), the same group is a syntax error at the colon, since a parenthesized group has no slots). Written directly after the text, a bare ask or .. is NOLA1020 instead.
Fix: compute the value first and interpolate the result, or move the ask into the function body. To use a call intent with a hint as a value, parenthesize it or write it in a ${} hole; a plain call with an extractor argument needs no parentheses.
// not-checked — WRONGdeclare function escalate(team: string): void;
export infer function route(.ticket: string) { `If the ticket is an outage,` escalate`page the on-call engineer`(..`the team to page`: string) return ask `the label for the ticket`: string;}// route.tsi — RIGHTdeclare function escalate(team: string): void;
export infer function route(.ticket: string) { `If the ticket is an outage,` (escalate`page the on-call engineer`(`the team to page`: string)) return ask `the label for the ticket`: string;}NOLA2011
Section titled “NOLA2011”exported type ‘…’ would also become a value, but a value named ‘…’ is already declared in this file — rename one of them
Cause: every exported type alias and interface in a .tsi also becomes a runtime value under the same name (Types as values), and this file already declares a const, let, var, function, class or enum with that name.
Fix: rename the type or the value.
NOLA2012
Section titled “NOLA2012”property ‘age’ of Signup: @minLength applies to strings, not to number
Cause: a JSDoc constraint tag (Constraints) does not fit — the keyword is for another kind of type, its value is missing or not a number, the @format name is unknown, the @pattern is not a valid regular expression, or the tag is given twice. Raised at the type for every kind of site (exported type, extractor, contextual parameter); the contextual-parameter policy does not apply, and prune mode does not drop the member.
Fix: move the tag to a member of the right kind, or correct its value.
NOLA2014
Section titled “NOLA2014”a typed template literal is an extractor only directly after
askor in a call’s argument list; write..`…`<T>here.
Cause: a template literal with type arguments — const i = `x`<string> — somewhere other than directly after ask or in a call’s slots (a direct argument, or a value nested in object/array literals there). The two dots are implied only in those two places; elsewhere a bare template is an ordinary string, so an extractor there needs its sigil (Two spellings, one construct). The colon spelling has no such code: const i = `x`: string is an ordinary syntax error.
Fix: write ..`x`: T (or ..`x`<T>) — for a stored intent, or an extractor under a ternary, a spread or new.
NOLA2015
Section titled “NOLA2015”Dept: Choice needs 2 to 255 labels, got 1
Cause: the criteria of a decision type are malformed — a Choice argument that is not a label union or a { label: description } type literal of string literals and null, fewer than 2 or more than 255 labels; a Scale argument that is not a tuple of 2 to 10 string literals; a Prob argument that is not exactly { true: "…"; false: "…" }. Raised at the type for every kind of site (exported type, extractor, contextual parameter); the contextual-parameter policy does not apply, and prune mode does not drop the member.
Fix: write the criteria as literals: Choice<{ billing: "Payments"; sales: null }>, Scale<["Calm", "Angry"]>, Prob<{ true: "…"; false: "…" }>.
NOLA2017
Section titled “NOLA2017”a context statement is only legal in a scope body — directly in an infer function or at module level. · a context statement cannot be the unbraced body of an
if, a loop or a label — wrap it in braces.
Cause: a bare template literal statement inside a plain function, a callback (even one written inside an infer function), a class static block or a namespace body, or as the unbraced body of an if, else, loop or label (if (x) `text`;). No ask can run there, so nothing could carry the text to the model.
Fix: move the statement directly into the infer function’s body or the module body, put braces around an unbraced body, or make the text a plain string if it was never meant for the model.
// not-checked — WRONGexport infer function go(.items: string[], urgent: boolean) { items.forEach((it) => { `note` it; // a callback: no ask can carry it }); if (urgent) `Answer quickly.`; // an unbraced body return ask `a summary`: string;}// go.tsi — RIGHTexport infer function go(.items: string[], urgent: boolean) { `items:` items; if (urgent) { `Answer quickly.` return ask `a short summary`: string; } return ask `a summary`: string;}Runtime errors (NOLA3xxx)
NOLA3001
Section titled “NOLA3001”This module was compiled for Nola emit contract N, but @nola-lang/runtime (at …) provides contract M. …
Cause: lowered code and the installed runtime disagree on the emit contract — a stale build or mismatched package versions.
Fix: the message says which: rebuild with the current nola-lang, or update @nola-lang/runtime. Keep the lockstep packages on one version.
NOLA3002
Section titled “NOLA3002”Two incompatible copies of @nola-lang/runtime are loaded: … Run
npm dedupe, or align your nola-lang versions so a single runtime is installed.
Cause: two runtimes with different emit contracts in one process.
Fix: npm dedupe; align every Nola package to the same version.
NOLA3003
Section titled “NOLA3003”(one of several config messages, e.g.)
a model map must include a default entry.·unknown config key x — allowed keys: …·Nola configuration is frozen after the first ask — call nolaRuntime.reset() before reconfiguring.·fallback([]) needs at least one model.·fallback() cannot wrap nola — the platform model can only be the root: it manages its own resilience (nola.infer({ retry }) for transport retry; routing and failover live on the server). record()/replay() remain available.
Cause: invalid config, reconfiguration after the first ask, an empty provider list, or an attempt to wrap the platform model in withRetry, fallback or roundRobin.
Fix: follow the file and field named in the message. See Config schema and Providers API.
The related message “No Nola model configured” is a NolaConfigError without a code. It means no config was loaded. Use nola run or the loader with a nola.config.ts, or call nolaRuntime.configure(…) before the first ask.
NOLA3004
Section titled “NOLA3004”forceModel “x” does not name a configured model — configured: … · .withModel() “x” does not name a configured model — configured: …
Cause: forceModel, ask with <name> or .withModel("name") names a key that is not in the model map. When the platform serves inference — model: "nola" — an ask-site name that matches no key is not an error: it is sent to the platform as an inference profile instead, so this code then fires only for forceModel.
Fix: add the model to the map, fix the name, or let the platform serve (model: "nola") so free-form names route through it.
NOLA3005
Section titled “NOLA3005”
pluginsis reserved for a future Nola version.
Cause: a plugins key in the config.
Fix: remove it.
NOLA3006
Section titled “NOLA3006”
cache must be an object — write cache: {} or cache: { store: <NolaCacheStore> }.·cache.store is not a NolaCacheStore (need { get(fingerprint), set(fingerprint, value) }).
Cause: a malformed cache section. Cache settings are validated but currently have no effect.
Fix: use cache: {} or a store with get and set methods.
NOLA3007
Section titled “NOLA3007”replay ledger … cannot be read: … · replay ledger …:N is not valid JSON. · replay ledger …:N is missing fingerprint or response.text.
Cause: the JSONL ledger given to replay(path) is missing, unreadable or malformed.
Fix: check the path; re-record the ledger. See Record and replay.
NOLA3008
Section titled “NOLA3008”replay ledger … has no entry for fingerprint … — the prompt, schema, or context changed since the ledger was recorded. Re-record it.
Cause: no recorded request matches the current prompt, contextual values, schema, params or inference profile. The provider name alone does not affect the match.
Fix: record the changed requests (NOLA_RECORD=1 with the setup in Record and replay), or switch to a live provider.
NOLA3009
Section titled “NOLA3009”this type cannot be used in an intent schema: …
Cause: an underivable type reached an ask at run time (a companion module marked it unsupported).
Fix: as for NOLA2002 — use a JSON-shaped type.
NOLA3010
Section titled “NOLA3010”extract/call intents carry no construction scope — only
asksupplies their frame.
Cause: a raw extractor or call intent was awaited or run outside ask, often from plain TypeScript.
Fix: resolve it with ask inside an infer function or at module level in a .tsi file. From plain TypeScript, await the infer function’s result.
// not-checked — WRONG: nothing supplies the inference contextimport { nameIntent } from "./person.tsi";const name = await nameIntent;// person.tsi — RIGHTexport const nameIntent = ..`the user's full name`: string;
export infer function whoIsIt(.text: string) { return ask nameIntent;}NOLA3011
Section titled “NOLA3011”Not raised — reserved.
NOLA3012
Section titled “NOLA3012”nola.config.ts cannot import “.tsi” modules (“…” imported from …) — the config is evaluated before the Nola loader registers.
Cause: nola.config.ts (or something it imports) imports a .tsi file.
Fix: keep the config graph plain TypeScript.
NOLA3013
Section titled “NOLA3013”Nola asks cannot execute in a browser context (server-only in v0). Move this call behind a server boundary (server component, route handler, server action).
Cause: an ask ran where window and document exist.
Fix: call the infer function from server code.
NOLA3015
Section titled “NOLA3015”the Nola loader needs Node.js module hooks, which Bun/Deno does not run. Run .tsi code on Node:
bun run start/npm start(the nola bin runs on Node) ornode --import nola-lang/register src/main.ts;bun --bunandbun src/main.tscannot load .tsi files.
Cause: the loader was started under Bun or Deno. Fix: run on Node (≥ 22.18); any package manager is fine.
NOLA3016
Section titled “NOLA3016”value does not match …: …
Cause: parse() on a type value rejected the input; the thrown NolaValidationError carries the structured issues.
Fix: validate with validate() when rejection is expected, or fix the input. See Validate and parse.
NOLA3017
Section titled “NOLA3017”JSON Schema target ‘…’ is not supported (expected one of ‘draft-2020-12’, ‘draft-07’, ‘openapi-3.0’)
Cause: a Standard JSON Schema consumer asked User["~standard"].jsonSchema.input() / .output() for a dialect Nola does not emit. The spec requires a library to throw rather than hand back the wrong dialect.
Fix: pass one of the three supported targets. See Standard Schema.
NOLA3018
Section titled “NOLA3018”output property “department” is a Choice; model “openai” cannot answer decision questions — route the ask to a decision model (
ask with <name>) or use the plain form (a literal union, or boolean).
Cause: the output type contains Choice, Scale or Prob, but the selected model does not support decision questions. Nola rejects the ask before sending a request because those types require probabilities from a decision model. A fallback or roundRobin group supports decisions if at least one member does; other members are skipped for decision asks.
Fix: select a decision model with ask with <name> or .withModel(). If you only need a label or yes/no answer, use a literal union or boolean.
Bundler errors (NOLA4xxx)
NOLA4001
Section titled “NOLA4001”.tsi modules are server-only in Nola v0 — this file is being bundled for a browser target. Move the import behind a server boundary (SSR entry, route handler, server action).
Cause: a client bundle imports a .tsi module.
Fix: import it from server code only (SSR entry, route handler, server action).
Next: Syntax cheatsheet