Manual error status handling

A Julia throw inside a --trim-compiled hand-written @ccallable does not become a Python exception — at best it aborts the host process. JuliaLibWrapping defines a convention for reporting errors across the C ABI so that generated wrappers can translate them into native exceptions.

An @api declaration generates this boundary automatically. This page is for authors designing an exact ABI by hand.

The JLWStatus convention

The canonical status struct is defined among the JLWInterop carriers subdir package and has the following shape:

struct JLWStatus
    code::Int32                       # 0 == success; non-zero == error
    message::NTuple{256, UInt8}       # UTF-8, null-terminated
end

A library opts in by either

  1. returning a JLWStatus directly, or
  2. returning a struct that contains a JLWStatus field (any field name).

Either form is recognized by the Python backend.

The inline buffer keeps JLWStatus allocation-free and avoids pointer ownership concerns. Messages are bounded to 255 bytes plus a NUL terminator.

Authoring a library

Add JLWInterop to your library's project, then construct status values with the provided helpers:

using JLWInterop

struct ResultStruct
    status::JLWStatus
    value::Float64
end

Base.@ccallable function compute(x::Float64)::ResultStruct
    x < 0 && return ResultStruct(jlw_error(1, "negative input"), 0.0)
    return ResultStruct(jlw_ok(), x * 2)
end

jlw_ok() and jlw_error(code, msg) perform no heap allocation, so they are safe in --trim builds.

What the Python wrapper does

For each entrypoint whose return type carries a JLWStatus (directly or via an embedded field), the generated wrapper checks status.code and raises a JLWError (a RuntimeError subclass, carrying .code and .message) when it is non-zero:

from mylib import compute, JLWError

try:
    result = compute(-1.0)
except JLWError as e:
    print(e.code, e.message)   # 1, "negative input"

On success the wrapper returns the full struct unchanged, including the status field (so callers can still inspect it explicitly if they prefer).

C backend

The C header generator does not currently auto-emit a status-check macro; C callers should check result.status.code themselves and read the message with, for example, printf("%.256s\n", result.status.message). A JLW_CHECK-style helper macro is a possible follow-up.