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.

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_NOT_IMPLEMENTED-4The requested operation is recognized but not implemented.
MAELYS_ERR_UNSUPPORTED-5The requested operation or domain is not supported.
MAELYS_ERR_TIMEOUT-6A bounded operation exceeded its time allowance.
MAELYS_ERR_IO-7A file read or write failure.
MAELYS_ERR_INTERNAL-8An internal allocation or engine failure.
MAELYS_ERR_UNAUTHORIZED-9The caller is not authenticated or authorized for the operation.
MAELYS_ERR_FORBIDDEN-10The operation is explicitly prohibited (e.g. a test-only policy loaded without the explicit flag).
MAELYS_ERR_RATE_LIMITED-11A provider or bounded service rejected the request because of rate limiting.
MAELYS_ERR_PAYLOAD_TOO_LARGE-12A static engine limit was exceeded (policy count, rule count, variable count, etc.).
MAELYS_ERR_INVALID_STATE-13An operation was attempted in a state that does not permit it.

Fail-closed model

CODE
Loader error
Parser error
Solver error
Query error
        ↓
       DENY

Example — 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:

CODE
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:

CODE
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-v2 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-v2
LEXER_UNSUPPORTED_CONSTRUCTunsupported numeric literalfloats are not supported by MAELYS-DATALOG-v2
LEXER_UNSUPPORTED_CONSTRUCTunsupported string escapestring escapes are not supported by MAELYS-DATALOG-v2
LEXER_UNSUPPORTED_CONSTRUCTunsupported Datalog constructunderscore is only supported as an isolated anonymous variable
LEXER_UNSUPPORTED_CONSTRUCTunsupported directiveconstruct is not supported by MAELYS-DATALOG-v2
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_INVALID_FILTERexpected filter argument listuse filter(Value, "constant pattern")
PARSER_INVALID_FILTERfilter value must be symbolicbind a symbol variable in a positive atom or use an allowed symbol
PARSER_INVALID_FILTERfilter pattern must be a string constantwrite the pattern as a quoted constant
PARSER_INVALID_FILTERfilter pattern exceeds capacityshorten the constant pattern
PARSER_INVALID_FILTERfilter program capacity exceededreduce the number of filter literals
PARSER_INVALID_FILTERfilter pattern pool capacity exceededreduce the total pattern bytes
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.
MAELYS_DATALOG_SOLVE_DIAG_FILTER_ERRORInvalid 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_FACTA fact is structurally invalid for the active predicate registry.
MAELYS_DATALOG_SOLVE_DIAG_MALFORMED_EDBThe EDB is not finalized, not sorted, or otherwise structurally invalid.
MAELYS_DATALOG_SOLVE_DIAG_INVALID_STATEThe ruleset or solve result is not in the state required by the operation.
MAELYS_DATALOG_SOLVE_DIAG_INVALID_ARGUMENTA required solve argument is invalid.
MAELYS_DATALOG_SOLVE_DIAG_INTERNAL_ERRORAn internal allocation or solver failure occurred.

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