ifURI docs

Error reference (error://)

urirun turns failures into standardized, addressable, searchable resources. Every failed execution is classified against established standards (not a bespoke taxonomy), gets a stable code and an error:// address, is recorded to a store, and links back to this page.

Standards used

ifURI does not invent its own error taxonomy. It reuses:

The error envelope

A failed result carries standardized fields:

{
  "uri": "shell://host/echo/run",
  "ok": false,
  "error": {
    "type": "policy",
    "message": "no allow rule matched (default deny)",
    "code": "E-ce9b1dd4",
    "category": "PERMISSION_DENIED",
    "severity": "warning",
    "status": 403,
    "uri": "error://local/E-ce9b1dd4/query/info",
    "help": "https://docs.ifuri.com/errors.html?code=E-ce9b1dd4&category=PERMISSION_DENIED#permission-denied"
  }
}

urirun.errors.problem(envelope) projects this to RFC 9457 application/problem+json (type = the help URL, instance = the error:// address, title = category, status, detail).

Categories

Every error maps to one canonical category.

CategoryHTTPSeverityMeaningFirst fix
INVALID_ARGUMENT400errorMalformed or invalid input, regardless of stateCheck the binding inputSchema and the payload
FAILED_PRECONDITION400errorSystem not in the required stateSatisfy the precondition / pass confirm=True
OUT_OF_RANGE400errorAttempted past the valid rangeUse a value inside the documented range
UNAUTHENTICATED401warningNo valid credentialsProvide auth before calling the route
PERMISSION_DENIED403warningNot allowed by the policy gateAdd an --allow rule matching the URI scope
NOT_FOUND404errorFile, route or binary not foundVerify the path, install the dep, or scan the binding
ALREADY_EXISTS409warningEntity to create already existsUse the existing entity or a new id
ABORTED409errorAborted, e.g. a concurrency conflictRetry with fresh state
RESOURCE_EXHAUSTED429warningQuota or resource limit hitFree resources or raise the limit
CANCELLED499noticeCancelled by the callerUsually expected; re-run if needed
DATA_LOSS500criticalUnrecoverable data loss/corruptionRestore from backup; investigate
UNKNOWN500errorUnmapped exceptionInspect the message; file a ticket
INTERNAL500errorInternal invariant broken (a bug)File a ticket with the code
UNIMPLEMENTED501errorNo adapter/executor for the routeCheck the binding adapter/kind
UNAVAILABLE503errorDependency/transport downRetry; check the node/service is reachable
DEADLINE_EXCEEDED504errorTimed out before completingRaise the timeout or check the target

How classification works

In order: an explicit errno, an errno name in the message, high-signal message patterns (more specific than a generic exception type), then the Python exception type, then weaker keywords. Examples of the type/errno mapping:

SourceCategory
policy (policy gate denial)PERMISSION_DENIED
confirm (needs confirmation)FAILED_PRECONDITION
schema, ValueError, KeyError, TypeErrorINVALID_ARGUMENT
FileNotFoundError, ENOENTNOT_FOUND
PermissionError, EACCES, EPERMPERMISSION_DENIED
TimeoutError, ETIMEDOUTDEADLINE_EXCEEDED
ConnectionError, ECONNREFUSEDUNAVAILABLE
NotImplementedError, "executor not found"UNIMPLEMENTED
FileExistsError, EEXISTALREADY_EXISTS
ENOSPC, EMFILERESOURCE_EXHAUSTED

Where errors are stored

Errors are appended to ~/.urirun/errors.jsonl.

Command line

urirun errors recent                  # same CLI, shorter operational form
urirun errors info E-ce9b1dd4
urirun errors search policy
urirun errors ticket E-ce9b1dd4 .
urirun errors bindings > error-bindings.json

python -m urirun.errors recent          # recent errors, aggregated by code
python -m urirun.errors info E-ce9b1dd4  # category, count, severity and fix hints
python -m urirun.errors search policy    # search by code, type, category, message, scheme
python -m urirun.errors ticket E-ce9b1dd4  # turn an error into a planfile ticket
python -m urirun.errors categories       # print the full category table

As URI flow resources

error:// is also a built-in URI resource. Add its bindings to any registry:

urirun errors bindings > error-bindings.json
urirun compile error-bindings.json --out error-registry.json

Then call it like any other URI route:

urirun run 'error://local/errors/query/recent' error-registry.json
urirun run 'error://local/errors/query/search' error-registry.json \
  --payload '{"query":"policy"}'
urirun run 'error://local/errors/query/info' error-registry.json \
  --payload '{"code":"E-ce9b1dd4"}'
urirun run 'error://local/errors/command/ticket' error-registry.json \
  --payload '{"code":"E-ce9b1dd4","project":"."}' \
  --execute --allow 'error://local/errors/command*'

The per-error address in each envelope is also executable:

urirun run 'error://local/E-ce9b1dd4/query/info' error-registry.json

In a YAML/JSON flow this gives you a normal diagnostic step:

- error://local/errors/query/search:
    query: policy
- error://local/errors/command/ticket:
    code: E-ce9b1dd4
    project: .

On a node

A running node records the errors it serves and exposes them over HTTP:

GET /errors                 # recent errors, aggregated by code
GET /errors/search?q=shell  # search the node's errors
GET /errors/<code>          # info + fix hints for one code

Every failed POST /run response carries the error:// address and help URL.

Capturing errors from selected functions

Route any function's exceptions into error:// with the @capture decorator - classification, code, address and recording happen automatically, and the exception still propagates (with exc.uri_error attached):

from urirun.errors import capture

@capture(scheme="dns")
def resolve(domain: str) -> list[str]:
    ...  # an exception here is classified, recorded and re-raised

# inspect after a failure:
try:
    resolve("example.com")
except Exception as exc:
    print(exc.uri_error["code"], exc.uri_error["category"], exc.uri_error["help"])

Use @capture(reraise=False) to return the standardized error envelope instead of raising.

From error to ticket

urirun.errors.to_ticket(code) (or python -m urirun.errors ticket <code>) creates a planfile ticket from a recorded error: it copies the category, severity, message, failing URIs, fix hints and the docs link into the ticket body, labels it error / urirun / <category> / <scheme>, and raises the priority to high once the error recurs five or more times (or its severity is critical+). This closes the loop from a runtime failure to tracked, fixable work.

Related