Python

Errors

Errors in the unified Python binding.

The Python errors reference continues the Python overview.

Exception and value classes

ClassKindRole
MaelysDatalogErrorException classException carrying a native status and structured diagnostic.
DiagnosticImmutable value classImmutable native detail record, separate from the operation status.
StatusEnumerationNamed native operation statuses, used when handling exceptions.

Properties and attributes

Read properties without parentheses. Their links lead to the class card defined on this page.

Property / protocolTypeMeaning
error.statusintNative operation status; compare with Status members.
error.codeintAlias of error.status; not the diagnostic reason code.
error.messagestrHuman-readable detail.
error.hintstrSuggested corrective action when supplied.
error.diagnosticDiagnosticStructured native detail, including its distinct reason code and presence mask.

Detailed API reference

Every class and method indexed at the start of this page has its detailed card here. A class’s method list also links to related operations documented on other topic pages; those links do not duplicate their cards.

Value-class declarations and public creation signatures below come from the published binding inventory. Resource-owning classes list their methods as individual links with a short explanation. Each link opens the complete signature, arguments, return behavior and example; the creation signatures are a reference, not executable class source. The examples are fragments: engine, ruleset, edb, session and result refer to the live objects explained on this page and in the complete examples. Import the named classes before using them.

MaelysDatalogError reference

Python · class
MaelysDatalogError
MaelysDatalogError(
    code: int,
    message: str,
    hint: str = '',
    *,
    diagnostic: Diagnostic | None = None,
) -> MaelysDatalogError
RuntimeError subclass raised on a failed native operation. status and code are the same integer operation status; message and hint are str; diagnostic is a Diagnostic record with its own reason code. Catch native failures separately from malformed Python arguments.
Example
from maelys_datalog import MaelysDatalogError, Status

try:
    edb.add_fact("owns", ["alice", "roadmap.pdf"])
    result = session.solve(edb)
except MaelysDatalogError as error:
    print(Status(error.status).name)
    print(error.message, error.hint)

Receiving a MaelysDatalogError

A failed native operation raises this exception. Application code normally catches it rather than constructs it; malformed Python arguments may instead raise TypeError, ValueError or OverflowError.

MaelysDatalogError attributes

Read these attributes without parentheses; they are not callable methods.

Attribute / protocolTypeMeaning
error.statusintNative operation status; compare with Status members.
error.codeintAlias of error.status; not the diagnostic reason code.
error.messagestrHuman-readable detail.
error.hintstrSuggested corrective action when supplied.
error.diagnosticDiagnosticStructured native detail, including its distinct reason code and presence mask.

Diagnostic reference

Python · class
Diagnostic
@dataclass(frozen=True)
class Diagnostic:
    source: int = 0
    status: int = 0
    code: int = 0
    present: int = 0
    line: int = 0
    column: int = 0
    phase: str = ''
    message: str = ''
    hint: str = ''
    file: str = ''
    predicate: str = ''
    arity: int = 0
    observed_count: int = 0
    limit: int = 0
    depth: int = 0
    depth_limit: int = 0
    rule_id: int = 0
    comparison_result: int = 0
    expected_kind: int = 0
    lhs_kind: int = 0
    rhs_kind: int = 0
    comparison_op: int = 0
    limit_kind: int = 0
    term_index: int = 0
    expected_arity: int = 0
    observed_arity: int = 0
    token: str = ''
    field: str = ''
    domain: str = ''
Immutable copy of native diagnostic fields, available as error.diagnostic. The present bitmask identifies which native detail sections are supplied; zero or empty defaults must not be mistaken for observed details.
Fields
sourceint
Native diagnostic source identifier.
statusint
Native operation status copied into the diagnostic.
codeint
Diagnostic reason code, distinct from the operation status.
presentint
Bitmask of supplied diagnostic detail sections.
lineint
Source line when location information is supplied.
columnint
Source column when location information is supplied.
phasestr
Operation phase reported by the engine.
messagestr
Human-readable diagnostic detail.
hintstr
Suggested corrective action when supplied.
filestr
Source or manifest path when supplied.
predicatestr
Predicate associated with the error.
arityint
Associated predicate arity.
observed_countint
Observed count for a supplied capacity diagnostic.
limitint
Capacity bound for that diagnostic.
depthint
Observed depth when depth information is supplied.
depth_limitint
Depth bound when supplied.
rule_idint
Associated rule identifier when supplied.
comparison_resultint
Reported comparison result when comparison details are supplied.
expected_kindint
Expected term kind when type details are supplied.
lhs_kindint
Kind of the left comparison operand.
rhs_kindint
Kind of the right comparison operand.
comparison_opint
Native comparison operator identifier.
limit_kindint
Identifier of the capacity that was exceeded.
term_indexint
Associated term position when supplied.
expected_arityint
Expected predicate arity.
observed_arityint
Observed argument count.
tokenstr
Token associated with the error.
fieldstr
Manifest or declaration field associated with the error.
domainstr
Domain associated with the error.
Example
try:
    ruleset = engine.load_manifest("policies/manifest.json")
except MaelysDatalogError as error:
    detail = error.diagnostic
    print(detail.phase, detail.message, detail.hint)
    print(detail.file, detail.line, detail.column)

Status reference

Python · enum
Status
class Status(IntEnum):
    OK = int(lib.MAELYS_DATALOG_STATUS_OK)
    INVALID_ARGUMENT = int(lib.MAELYS_DATALOG_STATUS_INVALID_ARGUMENT)
    INVALID_FIELD = int(lib.MAELYS_DATALOG_STATUS_INVALID_FIELD)
    NOT_FOUND = int(lib.MAELYS_DATALOG_STATUS_NOT_FOUND)
    NOT_IMPLEMENTED = int(lib.MAELYS_DATALOG_STATUS_NOT_IMPLEMENTED)
    UNSUPPORTED = int(lib.MAELYS_DATALOG_STATUS_UNSUPPORTED)
    TIMEOUT = int(lib.MAELYS_DATALOG_STATUS_TIMEOUT)
    IO = int(lib.MAELYS_DATALOG_STATUS_IO)
    INTERNAL = int(lib.MAELYS_DATALOG_STATUS_INTERNAL)
    UNAUTHORIZED = int(lib.MAELYS_DATALOG_STATUS_UNAUTHORIZED)
    FORBIDDEN = int(lib.MAELYS_DATALOG_STATUS_FORBIDDEN)
    RATE_LIMITED = int(lib.MAELYS_DATALOG_STATUS_RATE_LIMITED)
    PAYLOAD_TOO_LARGE = int(lib.MAELYS_DATALOG_STATUS_PAYLOAD_TOO_LARGE)
    INVALID_STATE = int(lib.MAELYS_DATALOG_STATUS_INVALID_STATE)
    STORAGE_TOO_SMALL = int(lib.MAELYS_DATALOG_STATUS_STORAGE_TOO_SMALL)
IntEnum for native operation statuses. Successful methods return their documented Python value, not Status.OK; failures raise MaelysDatalogError whose status can be compared with these members. A false membership answer is not an error status.
Example
from maelys_datalog import Status

if error.status == Status.UNSUPPORTED:
    print("The requested native feature is unsupported")

Usage and examples

Distinguish Python argument errors from native engine failures, then place both addition and solving inside an appropriate exception handler.

The reference above gives the exact declarations; the sections below explain their use.

Python exceptions

The following built-in exceptions are not additional binding classes. Their links explain the rejected input or lifecycle operation.

ExceptionMeaning
TypeErrorWrong Python representation.
ValueErrorInvalid value or embedded NUL.
OverflowErrorInteger outside signed 64-bit range.
RuntimeErrorLifecycle misuse; also the superclass of MaelysDatalogError.

Shape errors and native errors

An input can fail in two very different ways, from two different layers: a wrongly shaped Python call, or a well-shaped fact that the engine rejects. They raise different exception types, and they occur at different times in Python.

Shape errors — Python exceptions

Before buffering a fact, the wrapper checks its Python representation. It also validates query and enumeration arguments before passing them to C:

Input errorWhere checkedRaises
Predicate name is not a nonempty stringAddition/query/enumerationTypeError
terms is not a sequence, or is itself str/bytesAddition/queryTypeError
Unsupported term such as float or a legacy TermAddition/queryTypeError
Embedded NUL in a name or symbol stringAddition/queryValueError
Integer outside signed 64-bit rangeAddition/queryOverflowError
Too many terms for the public facadeAddition/queryValueError
Enumeration arity is not an integer, or is boolEnumerationTypeError
Enumeration arity is negative or exceeds the facade ceilingEnumerationValueError

The facade ceiling is not a declaration of each predicate's arity. A three-term owns fact can pass the Python shape check even if the domain declares owns/2. Exact vocabulary validation happens in the engine. Shape errors normally mean a caller bug: fix the call rather than treating them as a negative policy decision.

Engine errors — MaelysDatalogError

Native storage errors can now occur at addition: the total entry limit, individual text byte bounds and allocation failures. Unknown predicates, wrong declared arity, forbidden predicate kinds, symbol-pool and per-predicate limits remain solve-time checks. The buffer is independent of a selected policy. Keep the exception handler around both addition and solve.

MaelysDatalogError is a subclass of RuntimeError. Its .status and .code carry the facade's operation status; .message and .hint explain it. The full .diagnostic is separate. Do not import the old binding's C error namespace or assume its specialized registration exception classes exist here. See Diagnostics for the distinction between status and diagnostic code.

Catching both

The exceptions come from different layers: MaelysDatalogError is a RuntimeError, while TypeError, ValueError and OverflowError are not. An except MaelysDatalogError clause therefore does not catch a malformed Python call. Keep both addition and solve inside the protected block:

CODE
try:
    edb.add_fact(predicate, terms)   # Python shape + native storage validation.
    result = ruleset.solve(edb)     # Native vocabulary/capacity validation.
except (TypeError, ValueError, OverflowError):
    raise                          # Fix the caller's representation.
except MaelysDatalogError as exc:
    print(exc.status, exc.message, exc.hint)
    raise                          # Never turn an engine failure into ALLOW.

In practice the first clause is a development guard rail, while the second is where native runtime conditions land. A wrong owner thread or use after close is a separate lifecycle RuntimeError, not a policy decision.