C Low-level API
Errors
Legacy native result codes and detailed low-level diagnostics.Error codes
maelys_result_t return codes are returned by the low-level C functions on
this page. New consumers of <maelys/datalog.h> should use the separate
stable C errors reference and its
maelys_datalog_status_t names.
| Code | Value | Meaning |
|---|---|---|
MAELYS_OK | 0 | Success. |
MAELYS_ERR_INVALID_ARGUMENT | -1 | A required argument is null or structurally invalid. |
MAELYS_ERR_INVALID_FIELD | -2 | A field value has the wrong type, an invalid format, or a constraint violation (e.g. SHA-256 mismatch). |
MAELYS_ERR_NOT_FOUND | -3 | A referenced resource (file, policy ID, domain) does not exist. |
MAELYS_ERR_NOT_IMPLEMENTED | -4 | The requested operation is recognized but not implemented. |
MAELYS_ERR_UNSUPPORTED | -5 | The requested operation or domain is not supported. |
MAELYS_ERR_TIMEOUT | -6 | A bounded operation exceeded its time allowance. |
MAELYS_ERR_IO | -7 | A file read or write failure. |
MAELYS_ERR_INTERNAL | -8 | An internal allocation or engine failure. |
MAELYS_ERR_UNAUTHORIZED | -9 | The caller is not authenticated or authorized for the operation. |
MAELYS_ERR_FORBIDDEN | -10 | The operation is explicitly prohibited (e.g. a test-only policy loaded without the explicit flag). |
MAELYS_ERR_RATE_LIMITED | -11 | A provider or bounded service rejected the request because of rate limiting. |
MAELYS_ERR_PAYLOAD_TOO_LARGE | -12 | A static engine limit was exceeded (policy count, rule count, variable count, etc.). |
MAELYS_ERR_INVALID_STATE | -13 | An operation was attempted in a state that does not permit it. |
Fail-closed model
Loader error
Parser error
Solver error
Query error
↓
DENYExample — document access
The low-level documents.main program must distinguish a missing
authorization fact from an API failure. Both deny access, but only the former
is a successful negative query:
bool present = false;
maelys_result_t rc = maelys_datalog_query_solved_ground_fact(
result, "allow", terms, 2u, &present);
if (rc != MAELYS_OK) {
/* Failed query: log the code and deny. */
return DENY;
}
return present ? ALLOW : DENY;Here terms contains the two symbol IDs for Mallory and roadmap.pdf,
as shown on Querying.
ALLOW and DENY are application decisions, not engine results. A
loading or solving error is handled before this query. For the same request,
the stable API returns the corresponding maelys_datalog_status_t instead.
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 copied to error.diagnostic.message / error.diagnostic.hint — see
WASM bindings — Diagnostics.
The tables below document the diagnostics used by the public loading, parsing,
solving, and registry paths. A code can cover several situations, so it may
appear more than once with different messages. The enum and the
maelys_datalog_diag_code_name() / maelys_datalog_solve_diagnostic_category_name()
accessors remain the mechanical authority for the complete set supported by a
particular engine build.
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
| Code | message | hint |
|---|---|---|
MANIFEST_INVALID_JSON | invalid manifest JSON | fix manifest JSON syntax |
MANIFEST_INVALID_FIELD | invalid manifest field | policies must be an array |
MANIFEST_INVALID_FIELD | invalid required policy field | enabled policies require policy_id and file strings |
MANIFEST_INVALID_FIELD | invalid policy file path | policy files must use safe relative paths |
MANIFEST_INVALID_FIELD | invalid manifest field | predicate declarations must be arrays |
MANIFEST_INVALID_FIELD | invalid predicate declaration | predicate declarations require name and arity |
MANIFEST_INVALID_FIELD | invalid required policy field | provide all required policy fields with the expected type |
MANIFEST_INVALID_FIELD | invalid enabled field | enabled must be a boolean |
MANIFEST_INVALID_FIELD | invalid policy mode | mode must be enforce, shadow, or test_only |
MANIFEST_INVALID_FIELD | invalid policy SHA-256 field | sha256 must be lowercase hexadecimal |
MANIFEST_UNKNOWN_FIELD | unknown policy entry field | remove unsupported fields from the policy entry |
MANIFEST_UNKNOWN_DOMAIN | unknown policy domain | install a domain registry or disable the policy |
MANIFEST_SHA_MISMATCH | policy SHA mismatch | update manifest sha256 after policy edit |
MANIFEST_POLICY_NOT_FOUND | policy file not found | ensure the manifest policy file path exists |
MANIFEST_POLICY_NOT_FOUND | manifest file not found | ensure the manifest path exists |
MANIFEST_POLICY_NOT_FOUND | policy source not found | provide a bundle entry matching policy_id |
MANIFEST_TEST_ONLY_REJECTED | test-only policy rejected in production mode | load with test-only permission only for test manifests |
Lexer
| Code | message | hint |
|---|---|---|
LEXER_INVALID_TOKEN | unterminated block comment | close the block comment before end of file |
LEXER_INVALID_TOKEN | unterminated string literal | close the string literal before end of file |
LEXER_INVALID_TOKEN | invalid token | use :- to introduce a rule body |
LEXER_INVALID_TOKEN | invalid token | use MAELYS-DATALOG-v2 syntax |
LEXER_INVALID_TOKEN | expected term | use a variable, string atom, integer, or boolean term |
LEXER_INVALID_TOKEN | expected predicate argument list | add parentheses after the predicate name |
LEXER_INVALID_TOKEN | expected closing parenthesis | close the predicate argument list |
LEXER_INVALID_TOKEN | expected ')' in arithmetic expression | close the parenthesized arithmetic expression |
LEXER_INVALID_TOKEN | expected '(' after not | use not(predicate(X)) syntax |
LEXER_INVALID_TOKEN | expected ')' to close not() | close the not() form with a parenthesis |
LEXER_INVALID_TOKEN | unexpected token in rule body | expected a predicate atom or comparison |
LEXER_UNSUPPORTED_CONSTRUCT | unsupported Datalog construct | construct is not supported by MAELYS-DATALOG-v2 |
LEXER_UNSUPPORTED_CONSTRUCT | unsupported numeric literal | floats are not supported by MAELYS-DATALOG-v2 |
LEXER_UNSUPPORTED_CONSTRUCT | unsupported string escape | string escapes are not supported by MAELYS-DATALOG-v2 |
LEXER_UNSUPPORTED_CONSTRUCT | unsupported Datalog construct | underscore is only supported as an isolated anonymous variable |
LEXER_UNSUPPORTED_CONSTRUCT | unsupported directive | construct is not supported by MAELYS-DATALOG-v2 |
LEXER_UNSUPPORTED_CONSTRUCT | unsupported rule neck | use :- to introduce a rule body |
LEXER_INVALID_UTF8 | invalid UTF-8 in policy text | ensure the policy text is valid UTF-8 |
LEXER_STRING_TOO_LONG | string literal too long | shorten 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
| Code | message | hint |
|---|---|---|
PARSER_EXPECTED_PREDICATE | expected predicate | start facts and rule heads with a declared predicate |
PARSER_UNKNOWN_PREDICATE | unknown predicate | declare predicate in the domain registry |
PARSER_UNKNOWN_PREDICATE | predicate name too long | predicate names must be shorter than 64 characters |
PARSER_ARITY_MISMATCH | predicate arity exceeds maximum | reduce predicate arity or update the accepted predicate declaration |
PARSER_ARITY_MISMATCH | predicate arity mismatch | check registry or manifest arity |
PARSER_UNKNOWN_ATOM | unknown atom | register the atom in the domain registry |
PARSER_RULE_HEAD_EDB_FORBIDDEN | base predicate used in rule head | EDB/runtime and policy fact predicates cannot appear in rule heads |
PARSER_RULE_BODY_LITERAL_OVERFLOW | rule body literal limit exceeded | split rule into IDB helper predicates |
PARSER_UNSAFE_VARIABLE | comparison variable not bound by positive body atom | bind all comparison variables in positive body atoms first |
PARSER_UNSAFE_VARIABLE | negated atom variable not bound by positive body atom | bind all not() variables in positive body atoms first |
PARSER_UNSAFE_VARIABLE | head variable not bound by positive body atom | bind every head variable in a positive body atom |
PARSER_INVALID_COMPARISON | arithmetic expression node limit exceeded | simplify the arithmetic expression |
PARSER_INVALID_COMPARISON | arithmetic expression depth limit exceeded | reduce parentheses or split the expression |
PARSER_INVALID_COMPARISON | expected integer arithmetic expression | use integer literals, variables, +, -, *, and parentheses |
PARSER_INVALID_COMPARISON | expected comparison operator | use a supported comparison between typed terms |
PARSER_INVALID_COMPARISON | arithmetic comparison requires integer expressions | do not mix arithmetic expressions with symbols or booleans |
PARSER_INVALID_COMPARISON | expected comparison terms | use simple typed terms or integer arithmetic expressions |
PARSER_INVALID_COMPARISON | comparison term kind mismatch | compare terms of the same kind |
PARSER_INVALID_COMPARISON | invalid comparison operator for term kind | symbols and booleans support equality and inequality only |
PARSER_INVALID_FILTER | expected filter argument list | use filter(Value, "constant pattern") |
PARSER_INVALID_FILTER | filter value must be symbolic | bind a symbol variable in a positive atom or use an allowed symbol |
PARSER_INVALID_FILTER | filter pattern must be a string constant | write the pattern as a quoted constant |
PARSER_INVALID_FILTER | filter pattern exceeds capacity | shorten the constant pattern |
PARSER_INVALID_FILTER | filter program capacity exceeded | reduce the number of filter literals |
PARSER_INVALID_FILTER | filter pattern pool capacity exceeded | reduce the total pattern bytes |
PARSER_EXPECTED_DOT | expected clause terminator | terminate facts and rules with a dot |
PARSER_EXPECTED_NECK | expected rule neck | end direct facts with a dot or introduce a rule body with :- |
PARSER_FACT_USES_NON_BASE_PREDICATE | direct fact uses non-policy predicate | direct .dl facts must use policy fact predicates |
PARSER_ANONYMOUS_VARIABLE_IN_HEAD | anonymous variable is not allowed in rule heads | use named head variables bound by positive body atoms |
PARSER_ANONYMOUS_VARIABLE_IN_COMPARISON | anonymous variable is not allowed in comparisons | use a named variable bound by a positive body atom |
PARSER_ANONYMOUS_VARIABLE_IN_COMPARISON | anonymous variable is not allowed in arithmetic expressions | use a named variable bound by a positive body atom |
PARSER_ANONYMOUS_VARIABLE_IN_COMPARISON | anonymous variable not allowed in not() | bind variables via positive atoms before using not() |
PARSER_ANONYMOUS_VARIABLE_IN_FACT | anonymous variable is not allowed in direct facts | direct .dl facts must be ground policy facts |
PARSER_ANONYMOUS_VARIABLE_IN_FACT | variables are not allowed in direct facts | direct .dl facts must be ground policy facts |
PARSER_TOO_MANY_VARIABLES | rule variable limit exceeded | reduce anonymous variables or split the rule |
Policy
| Code | message | hint |
|---|---|---|
POLICY_NOT_STRATIFIABLE | negation stratum limit exceeded | remove recursion through negation or reduce negation depth |
POLICY_NOT_STRATIFIABLE | negation stratum limit exceeded | reduce stratification depth below MAELYS_DATALOG_MAX_STRATA |
POLICY_NOT_STRATIFIABLE | policy is not stratifiable | remove recursion through negation |
Solver
| Code | Meaning |
|---|---|
MAELYS_DATALOG_SOLVE_DIAG_COMPARISON_TYPE_ERROR | Runtime 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. |
MAELYS_DATALOG_SOLVE_DIAG_FILTER_ERROR | Invalid filter program, runtime value, or exhausted filter budget. Always a hard DENY; an ordinary non-match is not an error. |
MAELYS_DATALOG_SOLVE_DIAG_MALFORMED_FACT | A fact is structurally invalid for the active predicate registry. |
MAELYS_DATALOG_SOLVE_DIAG_MALFORMED_EDB | The EDB is not finalized, not sorted, or otherwise structurally invalid. |
MAELYS_DATALOG_SOLVE_DIAG_INVALID_STATE | The ruleset or solve result is not in the state required by the operation. |
MAELYS_DATALOG_SOLVE_DIAG_INVALID_ARGUMENT | A required solve argument is invalid. |
MAELYS_DATALOG_SOLVE_DIAG_INTERNAL_ERROR | An internal allocation or solver failure occurred. |
Registry
| Code | message | hint |
|---|---|---|
REGISTRY_CONFLICT | query whitelist predicate rejected | queries must reference domain QUERY predicates with matching arity |
REGISTRY_CONFLICT | query whitelist predicate is not query-capable | queries may only expose predicates marked QUERY by the domain |
REGISTRY_CONFLICT | domain predicate registry install failed | check domain predicate declarations |
REGISTRY_MUTATION_AFTER_FREEZE | predicate registry is not frozen | freeze the registry before parsing policy text |