Error codes

maelys_result_t return codes are returned by every API function.

CodeValueMeaning
MAELYS_OK0Success.
MAELYS_ERR_INVALID_ARGUMENT-1A required argument is null or structurally invalid.
MAELYS_ERR_INVALID_FIELD-2A field value has the wrong type, an invalid format, or a constraint violation (e.g. SHA-256 mismatch).
MAELYS_ERR_NOT_FOUND-3A referenced resource (file, policy ID, domain) does not exist.
MAELYS_ERR_UNSUPPORTED-7The requested operation or domain is not supported.
MAELYS_ERR_FORBIDDEN-8The operation is explicitly prohibited (e.g. a test-only policy loaded without the explicit flag).
MAELYS_ERR_PAYLOAD_TOO_LARGE-9A static engine limit was exceeded (policy count, rule count, variable count, etc.).
MAELYS_ERR_IO-10A file read or write failure.
MAELYS_ERR_INTERNAL-11An internal allocation or engine failure.
MAELYS_ERR_INVALID_STATE-13An operation was attempted in a state that does not permit it.
!

All loader, parser, solver, and query failures must be interpreted as DENY.

There is no valid path from an error to an authorization.

Fail-closed model

Loader error
Parser error
Solver error
Query error

       DENY

Diagnostic codes

When a function accepts a maelys_datalog_diagnostic_t *out_diag parameter, it populates the diagnostic on failure with a precise code and context fields. Diagnostics do not change behavior — loading remains fail-closed.

Every diagnostic carries both a message and a corrective hint field. In WASM, both are accessible as pg.diagMessage / pg.diagHint — see WASM bindings — Diagnostics.

The tables below are exhaustive: every message/hint pair below was verified first-hand against the engine source (every call site that sets a maelys_datalog_diag_code_t, across the lexer, parser, manifest loader, and registry). One code can cover several distinct situations — each row is a distinct verified message/hint pair; the code repeats when it covers more than one situation. Rows are listed in source order, not alphabetically.

To convert a code to a string for logging:

const char *name = maelys_datalog_diag_code_name(diag.code);
/* e.g. "MANIFEST_SHA_MISMATCH" */

Manifest

Codemessagehint
MANIFEST_INVALID_JSONinvalid manifest JSONfix manifest JSON syntax
MANIFEST_INVALID_FIELDinvalid manifest fieldpolicies must be an array
MANIFEST_INVALID_FIELDinvalid required policy fieldenabled policies require policy_id and file strings
MANIFEST_INVALID_FIELDinvalid policy file pathpolicy files must use safe relative paths
MANIFEST_INVALID_FIELDinvalid manifest fieldpredicate declarations must be arrays
MANIFEST_INVALID_FIELDinvalid predicate declarationpredicate declarations require name and arity
MANIFEST_INVALID_FIELDinvalid required policy fieldprovide all required policy fields with the expected type
MANIFEST_INVALID_FIELDinvalid enabled fieldenabled must be a boolean
MANIFEST_INVALID_FIELDinvalid policy modemode must be enforce, shadow, or test_only
MANIFEST_INVALID_FIELDinvalid policy SHA-256 fieldsha256 must be lowercase hexadecimal
MANIFEST_UNKNOWN_FIELDunknown policy entry fieldremove unsupported fields from the policy entry
MANIFEST_UNKNOWN_DOMAINunknown policy domaininstall a domain registry or disable the policy
MANIFEST_SHA_MISMATCHpolicy SHA mismatchupdate manifest sha256 after policy edit
MANIFEST_POLICY_NOT_FOUNDpolicy file not foundensure the manifest policy file path exists
MANIFEST_POLICY_NOT_FOUNDmanifest file not foundensure the manifest path exists
MANIFEST_POLICY_NOT_FOUNDpolicy source not foundprovide a bundle entry matching policy_id
MANIFEST_TEST_ONLY_REJECTEDtest-only policy rejected in production modeload with test-only permission only for test manifests

Lexer

Codemessagehint
LEXER_INVALID_TOKENunterminated block commentclose the block comment before end of file
LEXER_INVALID_TOKENunterminated string literalclose the string literal before end of file
LEXER_INVALID_TOKENinvalid tokenuse :- to introduce a rule body
LEXER_INVALID_TOKENinvalid tokenuse MAELYS-DATALOG-TEXT-v1 syntax
LEXER_INVALID_TOKENexpected termuse a variable, string atom, integer, or boolean term
LEXER_INVALID_TOKENexpected predicate argument listadd parentheses after the predicate name
LEXER_INVALID_TOKENexpected closing parenthesisclose the predicate argument list
LEXER_INVALID_TOKENexpected ')' in arithmetic expressionclose the parenthesized arithmetic expression
LEXER_INVALID_TOKENexpected '(' after notuse not(predicate(X)) syntax
LEXER_INVALID_TOKENexpected ')' to close not()close the not() form with a parenthesis
LEXER_INVALID_TOKENunexpected token in rule bodyexpected a predicate atom or comparison
LEXER_UNSUPPORTED_CONSTRUCTunsupported Datalog constructconstruct is not supported by MAELYS-DATALOG-TEXT-v1
LEXER_UNSUPPORTED_CONSTRUCTunsupported numeric literalfloats are not supported by MAELYS-DATALOG-TEXT-v1
LEXER_UNSUPPORTED_CONSTRUCTunsupported string escapestring escapes are not supported by MAELYS-DATALOG-TEXT-v1
LEXER_UNSUPPORTED_CONSTRUCTunsupported Datalog constructunderscore is only supported as an isolated anonymous variable
LEXER_UNSUPPORTED_CONSTRUCTunsupported directiveconstruct is not supported by MAELYS-DATALOG-TEXT-v1
LEXER_UNSUPPORTED_CONSTRUCTunsupported rule neckuse :- to introduce a rule body
LEXER_INVALID_UTF8invalid UTF-8 in policy textensure the policy text is valid UTF-8
LEXER_STRING_TOO_LONGstring literal too longshorten the string atom or register a bounded atom

Note: LEXER_INVALID_UTF8 is verified as emitted from the manifest loader (maelys_datalog_manifest_buffer.c), as a pre-lex validation step — not from maelys_datalog_lexer.c itself.

Parser

Codemessagehint
PARSER_EXPECTED_PREDICATEexpected predicatestart facts and rule heads with a declared predicate
PARSER_UNKNOWN_PREDICATEunknown predicatedeclare predicate in the domain registry
PARSER_UNKNOWN_PREDICATEpredicate name too longpredicate names must be shorter than 64 characters
PARSER_ARITY_MISMATCHpredicate arity exceeds maximumreduce predicate arity or update the accepted predicate declaration
PARSER_ARITY_MISMATCHpredicate arity mismatchcheck registry or manifest arity
PARSER_UNKNOWN_ATOMunknown atomregister the atom in the domain registry
PARSER_RULE_HEAD_EDB_FORBIDDENbase predicate used in rule headEDB/runtime and policy fact predicates cannot appear in rule heads
PARSER_RULE_BODY_LITERAL_OVERFLOWrule body literal limit exceededsplit rule into IDB helper predicates
PARSER_UNSAFE_VARIABLEcomparison variable not bound by positive body atombind all comparison variables in positive body atoms first
PARSER_UNSAFE_VARIABLEnegated atom variable not bound by positive body atombind all not() variables in positive body atoms first
PARSER_UNSAFE_VARIABLEhead variable not bound by positive body atombind every head variable in a positive body atom
PARSER_INVALID_COMPARISONarithmetic expression node limit exceededsimplify the arithmetic expression
PARSER_INVALID_COMPARISONarithmetic expression depth limit exceededreduce parentheses or split the expression
PARSER_INVALID_COMPARISONexpected integer arithmetic expressionuse integer literals, variables, +, -, *, and parentheses
PARSER_INVALID_COMPARISONexpected comparison operatoruse a supported comparison between typed terms
PARSER_INVALID_COMPARISONarithmetic comparison requires integer expressionsdo not mix arithmetic expressions with symbols or booleans
PARSER_INVALID_COMPARISONexpected comparison termsuse simple typed terms or integer arithmetic expressions
PARSER_INVALID_COMPARISONcomparison term kind mismatchcompare terms of the same kind
PARSER_INVALID_COMPARISONinvalid comparison operator for term kindsymbols and booleans support equality and inequality only
PARSER_EXPECTED_DOTexpected clause terminatorterminate facts and rules with a dot
PARSER_EXPECTED_NECKexpected rule neckend direct facts with a dot or introduce a rule body with :-
PARSER_FACT_USES_NON_BASE_PREDICATEdirect fact uses non-policy predicatedirect .dl facts must use policy fact predicates
PARSER_ANONYMOUS_VARIABLE_IN_HEADanonymous variable is not allowed in rule headsuse named head variables bound by positive body atoms
PARSER_ANONYMOUS_VARIABLE_IN_COMPARISONanonymous variable is not allowed in comparisonsuse a named variable bound by a positive body atom
PARSER_ANONYMOUS_VARIABLE_IN_COMPARISONanonymous variable is not allowed in arithmetic expressionsuse a named variable bound by a positive body atom
PARSER_ANONYMOUS_VARIABLE_IN_COMPARISONanonymous variable not allowed in not()bind variables via positive atoms before using not()
PARSER_ANONYMOUS_VARIABLE_IN_FACTanonymous variable is not allowed in direct factsdirect .dl facts must be ground policy facts
PARSER_ANONYMOUS_VARIABLE_IN_FACTvariables are not allowed in direct factsdirect .dl facts must be ground policy facts
PARSER_TOO_MANY_VARIABLESrule variable limit exceededreduce anonymous variables or split the rule

Policy

Codemessagehint
POLICY_NOT_STRATIFIABLEnegation stratum limit exceededremove recursion through negation or reduce negation depth
POLICY_NOT_STRATIFIABLEnegation stratum limit exceededreduce stratification depth below MAELYS_DATALOG_MAX_STRATA
POLICY_NOT_STRATIFIABLEpolicy is not stratifiableremove recursion through negation

Solver

CodeMeaning
MAELYS_DATALOG_SOLVE_DIAG_COMPARISON_TYPE_ERRORRuntime cross-type comparison: a variable resolved to a type incompatible with the operator. Always a hard DENY. solve_once_ex() returns MAELYS_ERR_INVALID_FIELD, result is NULL. Diagnostic fields: lhs_kind, rhs_kind, comparison_op. Denial reason: MAELYS_DATALOG_DENY_COMPARISON_TYPE_ERROR. See Comparison operators.
i

MAELYS_DATALOG_DIAG_RUNTIME_INVALID_COMPARISON exists in the maelys_datalog_diag_code_t enum but is not currently emitted by any traced code path in the lexer, parser, manifest loader, or registry (verified by an exhaustive search across src/core, src/manifest, and src/wasm). It is a separate enum member from SOLVE_DIAG_COMPARISON_TYPE_ERROR above, which belongs to solve_once_ex()'s own lightweight diagnostic struct, not this one. Documented here for completeness rather than omitted silently.

Registry

Codemessagehint
REGISTRY_CONFLICTquery whitelist predicate rejectedqueries must reference domain QUERY predicates with matching arity
REGISTRY_CONFLICTquery whitelist predicate is not query-capablequeries may only expose predicates marked QUERY by the domain
REGISTRY_CONFLICTdomain predicate registry install failedcheck domain predicate declarations
REGISTRY_MUTATION_AFTER_FREEZEpredicate registry is not frozenfreeze the registry before parsing policy text