Skip to content

@askdb/core API reference: errors

Reference · @askdb/core

What @askdb/core throws, what throws it, and the code each error carries.

@askdb/core’s errors are AskDbError or one of its subclasses, so instanceof AskDbError catches them, except the few plain Errors listed after the table. Each class sets name to its class name. @askdb/client adds four more; see @askdb/client API → Errors. Fix common errors has fixes for the common codes.

ClassThrown byFields
AskDbErrorThe base class. Thrown as is by parseAskDbModeV1() for an unknown mode, and by ask() and generateSelectSql() with a built-in dialect or DialectSpec for an empty question or a schema with no tables.message, cause?
SchemaParseErrorloadSchema(), loadSchemaFromJson(), readSchemaDirectory() and the markdown parsers (parseTableMarkdown(), parseConceptsMarkdown(), parseTenantPolicyMarkdown()): a file that can’t be read or parsed, a name in the wrong case, a standalone JSON file in a schema directory, an invalid tenant policy, or, under enforcement: strict, a tenant policy that names IDs schema.json doesn’t have or a column on another table than its entry’s. See loadSchema(path). Also ask(), resolveTenantSql() and validateTenantScope() for a policy built in code whose two roots share a placeholder.cause?
UnknownDialectErrorask() with a string dialect that isn’t a built-in id, or a DialectSpec whose id isn’t one.dialectId
SqlGenerationErrorask() and generateSelectSql() with a built-in dialect or DialectSpec, when the model call fails or is aborted. Its message is Model call failed: followed by the failure’s message. When that message gives no reason, as for a refused connection, AskDB adds the code and address of each error under it, such as ECONNREFUSED 127.0.0.1:11434, or the error’s name when it has no code.cause: the AI SDK’s error or the abort reason.
SqlValidationErrorvalidateSelectSql(), and a deny from the read-only check in ask(), generateSelectSql() and bindPreparedQuery().rule, hint?, verdict?
TenantScopeErrorask(), bindPreparedQuery(), expandTenantScope(), validateTenantScope(), resolveTenantSql() and buildTenantPromptBlock(): a missing or invalid scope, or tenant IDs that can’t be bound.reason
TenantGuardrailErrorA deny from the tenant check in ask(), generateSelectSql() and bindPreparedQuery(), and validateTenantGuardrails() under a strict policy.warnings, verdict?
SensitiveReferenceErrorvalidateSensitiveReferences() in strict mode, and a deny from the sensitive check in ask() and bindPreparedQuery().rule, references, unresolvedScope?, verdict?
QueryParameterErrorbindPreparedQuery(): missing guardrails inputs, a template that declares a parameter type or cardinality AskDB doesn’t support, or values that don’t fit the template.reason
SensitiveEnrichmentTargetErrorsuggestEnrichment(), for a sensitive target column while omitSensitiveIdentifiersFromPrompt is on. The message names no column.—

verdict is set when the error comes from ask(), generateSelectSql() or bindPreparedQuery(): every finding of every check, not only the one that denied. See Guardrail verdict. A direct validateSelectSql() or validateSensitiveReferences() call leaves it unset.

Some functions throw a plain Error instead: getDialectSpec() for an id with no spec, saveSchemaFile() for a path it refuses (see saveSchemaFile), and suggestEnrichment() for a target column the table doesn’t have. A custom AskDialect’s errors pass through ask() unchanged.

rule is a SqlValidationRuleCode:

RuleThe SQL has
SQL_EMPTYNo statement.
SQL_MULTI_STATEMENTMore than one statement.
SQL_COMMENTA --, /* */ or (MySQL) # comment.
SQL_NOT_SELECT_OR_WITHA first keyword other than SELECT or WITH.
SQL_FORBIDDEN_KEYWORDA keyword from the shared denylist or the dialect’s extraForbiddenKeywords, outside strings and quoted identifiers. Also a $N parameter written against a word ($1INTO), where the SQL may be read as Postgres.
SQL_FORBIDDEN_FUNCTIONA call to a function in the dialect’s blockedFunctions, such as pg_sleep( or, on MySQL and MariaDB, NEXTVAL(. On every dialect, also NEXT VALUE FOR, which advances a sequence on MariaDB and SQL Server.
SQL_UNICODE_ESCAPED_IDENTIFIEROn Postgres and CockroachDB, a Unicode-escaped identifier such as U&"set\005fconfig", whose escapes could spell a blocked function or a sensitive column.
SQL_UNTERMINATEDA string, quoted identifier, dollar-quoted string or block comment that never closes.
SQL_STRING_CONTINUATIONOn Postgres, CockroachDB, or a dialect id that is no built-in engine, an E'…' string continued on a later line (E'a', a newline, then 'b'). Postgres reads the continuation with backslash escapes, so the checks can’t tell where the string ends.
SQL_ESCAPED_BYTE_STRINGOn Postgres, CockroachDB, or a dialect id that is no built-in engine, a b'…' byte string (lower-case b) with a backslash, or continued on a later line. CockroachDB reads it with backslash escapes, so the checks can’t tell where the string ends. Write the bytes as a hex string cast to bytea ('\x00'::bytea).
SQL_LOCKING_READA locking read: FOR UPDATE, FOR SHARE, LOCK IN SHARE MODE, or a T-SQL lock hint such as UPDLOCK. On SQL Server, also a name that isn’t printable ASCII, unquoted or quoted alone in parentheses after a name, which SQL Server can read as a hint.

hint explains why the rule exists and how to fix the SQL. The read-only check says what each rule rejects on each engine.

reason is a TenantScopeRejectionReason:

ReasonCause
MISSING_SCOPEThe schema has a tenant policy and no tenantScope was passed.
INVALID_SCOPE_SHAPEtenantScope doesn’t match TenantScope, for example an ids scope with no IDs.
UNKNOWN_TENANT_ROOTtenantRoot isn’t a root in the policy.
GLOBAL_WITHOUT_REASONA global scope whose reason is missing, isn’t a string, or is blank. It’s checked before the shape, so such a scope never throws INVALID_SCOPE_SHAPE.
SUBTREE_NOT_RESOLVABLEA subtree scope can’t be expanded: no resolveTenantDescendants, or it returned something other than IDs per root in the subtree. Also an unexpanded subtree passed to bindPreparedQuery(), resolveTenantSql() or buildTenantPromptBlock().
UNRESOLVED_TENANT_PLACEHOLDERThe SQL has a :tenant_<root>_ids placeholder the scope has no IDs for, including any placeholder under a global scope.
UNSUPPORTED_TENANT_PREDICATEA scope with several IDs meets a tenant predicate with no list form, such as <=.
UNESCAPABLE_TENANT_IDA tenant ID that isn’t printable ASCII (U+0020 to U+007E), or that contains a backslash, would be inlined as a literal: in "sql-only" mode, by ask(), resolveTenantSql() or bindPreparedQuery().
TENANT_SQL_MODE_REQUIREDask() has a tenant scope, no tenantSqlMode, and a custom AskDialect. Also a tenantSqlMode other than "sql-params" or "sql-only", on every dialect, in ask() (with a tenant scope), bindPreparedQuery() and resolveTenantSql().

warnings is a TenantGuardrailWarning[], each { rule, tableId, message }. rule is a TenantGuardrailRuleCode:

RuleThe SQL
MISSING_TENANT_PREDICATEReads a tenant-scoped table, or a root table the scope covers, with no tenant predicate that filters it. See What the tenant check requires.
MISSING_TYPE_DISCRIMINATORReads a polymorphic table without its type column compared with the mapping key of the root whose placeholder filters its ID column, ANDed into the same clause (owner_type = 'agency' AND owner_id = :tenant_agency_ids).
UNKNOWN_TABLE_REFERENCEDReads a table that tenant-policy.md doesn’t classify.
UNICODE_ESCAPED_IDENTIFIERUses a name the check can’t match as written, as a table, column or function. Either a Postgres Unicode-escaped name, such as U&"\006Frders", which Postgres decodes, reported when the SQL may be read as Postgres: postgres, cockroachdb, a custom AskDialect or no dialect. Or a name with a character outside printable ASCII that the schema doesn’t spell exactly, such as [orders], which SQL Server’s collation reads as orders, reported under sqlserver, a custom AskDialect or no dialect. Or a name the schema doesn’t spell exactly that holds a letter some engine reads as an ASCII letter (U+0130, U+0131, U+017F, U+212A) or that isn’t in NFC, as written or, unquoted, once lower-cased, which CockroachDB normalizes names to, reported under every dialect. The schema’s spelling excuses an unquoted one only when it is lower-case and in NFC, as CockroachDB reads it: an unquoted İTEMS reads items. Reported with tableId: "".
UNPROVABLE_SCOPEReported for a custom AskDialect whose tenantGuardrail has passed: false and no warnings.
NOT_SELECT_OR_WITHHas a statement that doesn’t start with SELECT or WITH (after any comments and opening parentheses), such as a DO block, CALL, EXPLAIN or SHOW, so the check can’t read what it touches. Every statement counts: each one after a ;, and on SQL Server each one a verb such as EXEC starts with no ;. For a custom AskDialect, every engine’s reading counts. Under sqlserver, also code SQL Server may split into tokens differently, outside strings, quoted names and comments (see SQL Server statements). Not checked under a global scope. Reported for the whole SQL (tableId: ""), under every dialect, a custom AskDialect included.
UNCHECKABLE_FUNCTIONNames a function whose reads the check can’t see: one that runs a query passed as a string (query_to_xml, ts_stat, crosstab), reads a table named in a string (table_to_xml, schema_to_xml, connectby), runs SQL over another connection (dblink, OPENQUERY, OPENROWSET), or sets a value row-level security may read (set_config). The tenant policy contract lists the 32 functions, each built into Postgres or SQL Server or from the tablefunc, dblink or xml2 extension; a function from any other extension, such as pgRouting’s, passes. A listed name counts in any code position, so a column, alias or parameter named like one (crosstab, set_config) is refused too. Only the first listed name is reported. Not checked under a global scope. Reported for the whole SQL (tableId: ""), under every dialect, a custom AskDialect included.
STRING_CONTINUATIONContinues an E'…' string on a later line, which Postgres reads with backslash escapes and the check doesn’t, so a table or call after it could be hidden. Reported with tableId: "" when the SQL may be read as Postgres: postgres, cockroachdb, a custom AskDialect or no dialect.
ESCAPED_BYTE_STRINGHas a b'…' byte string (lower-case b) with a backslash, or continued on a later line, which CockroachDB reads with backslash escapes and the check doesn’t, so a table or call after it could be hidden. Reported with tableId: "" when the SQL may be read as Postgres: postgres, cockroachdb, a custom AskDialect or no dialect.

The policy’s enforcement decides the outcome: strict throws TenantGuardrailError, and warn returns the findings in result.verdict. At rebind, a warn finding throws too, unless acceptWarnings includes "tenant". The tenant check says when it runs and how it reads SQL on each engine.

rule is a SensitiveReferenceRuleCode: SENSITIVE_TABLE_REFERENCED, SENSITIVE_COLUMN_REFERENCED, or UNRESOLVED_TABLE_SCOPE when no sensitive reference was found but the table scope couldn’t be resolved. references lists each SensitiveReference, and unresolvedScope is set when scope resolution was incomplete (see Conservative failure). The sensitive check says what it reports and the forms it can miss.

reason is a QueryParameterRejectionReason. bindPreparedQuery() throws every one:

ReasonCause
INVALID_VALUEA value doesn’t match its parameter’s type or cardinality: a list for a scalar, a non-finite number, a date that isn’t YYYY-MM-DD, or null.
MISSING_VALUEvalues has no entry for a declared parameter.
UNRESOLVED_PLACEHOLDERA declared parameter doesn’t appear in namedSql, a placeholder has no declaration, or a placeholder is left after binding.
INVALID_LIST_CONTEXTA list parameter isn’t alone inside IN (…), or = ANY(…) on a dialect with listBinding: "array".
DIALECT_UNSUPPORTEDThe template’s dialect isn’t a built-in id, or guardrails.dialect has a different id.
MISSING_GUARDRAIL_CONTEXTNo third argument, or no schema in it.
TENANT_VALUE_SUPPLIEDvalues has a :tenant_* key. Tenant IDs come from tenantScope only.
TENANT_PARAM_ALIGNMENTThe tenant markers in unboundSql can’t be lined up with the business parameters.
DIALECT_SPEC_REQUIREDNo guardrails.dialect for a template with dialectRules: "host", or one stored without dialectRules.
INVALID_DECLARATIONA parameter in the template declares a type other than string, number, boolean, date or datetime, or a cardinality other than one or many, or the template’s parameters isn’t a list of declarations. Checked before any value; regenerate the template.