Skip to main content

Error Codes

Machine-readable error codes returned by the management API in the error envelope (error.code), with their HTTP status. The /v1 OpenAI-compatible endpoints translate the same conditions into OpenAI-format errors.

Model errors

CodeHTTPMeaning
MODEL_NOT_FOUND404No model with that ID
MODEL_ALREADY_EXISTS409Same repo + quantization already downloaded
MODEL_LOAD_FAILED500Load crashed — see server logs
MODEL_NOT_LOADED400Operation needs a loaded model
MODEL_ALREADY_LOADED400Load called on the loaded model
MODEL_BUSY409Operation conflicts with one in progress
MODEL_LOCKED409Unload/delete refused; detach the SAE or unlock first

Resource errors

CodeHTTPMeaning
INSUFFICIENT_MEMORY507Estimated VRAM exceeds what's free
INSUFFICIENT_DISK507Not enough disk for the download

Download errors

CodeHTTPMeaning
DOWNLOAD_FAILED502Upstream (HuggingFace) failure
DOWNLOAD_CANCELLED499Cancelled by user
REPO_NOT_FOUND404Repository doesn't exist
GATED_MODEL_NO_TOKEN401Gated repo, no token — accept the license and supply hf_token
INVALID_HF_TOKEN401Token rejected by HuggingFace
INVALID_LOCAL_PATH400Local import path missing, malformed, or in a protected system directory

SAE & steering errors

CodeHTTPMeaning
SAE_NOT_FOUND404No SAE with that ID
SAE_NOT_ATTACHED400Steering/monitoring requires an attached SAE
SAE_ALREADY_ATTACHED409That exact (sae_id, layer) is already attached (re-attach is rejected; multi-SAE attach_set on other layers is fine); also raised deleting an attached SAE
SAE_INCOMPATIBLE400d_in doesn't match the loaded model's hidden size
SAE_LOAD_FAILED500Weight loading crashed
INVALID_FEATURE_INDEX400Feature index outside [0, d_sae)details names the offender
SAE_SET_INCOMPLETE422Serving a cross-layer circuit but a member's layer has no (unique) attached SAE — details.offenders names each {feature_idx, layer, sae_id?, reason?} (Feature 12 multi-SAE)
Steering value range: reject on set, clamp on dial

The two paths differ. Setting a steering value directly — POST /api/saes/steering and /steering/batch — validates against [-200, 200], so an out-of-range value is rejected with 422 VALIDATION_ERROR (the schema bound), not silently clamped. The ±200 clamp applies only on the dial/intensity path — profile activation and per-request/cluster/circuit λ — where a profile's stored strengths are scaled by λ and the scaled result is clamped to ±200 at apply time rather than failing the request. So a value you type is checked; a value the dial produces is clamped.

Profile errors

CodeHTTPMeaning
PROFILE_NOT_FOUND404Unknown profile ID/name (including the per-request profile parameter)
PROFILE_ALREADY_EXISTS409Duplicate name
PROFILE_INCOMPATIBLE400Profile can't apply to the current configuration
VALIDATION_ERROR200†Malformed import payload — steering entries that can't convert to int→float (returned in the envelope, details.invalid_keys names them)
IS_CLUSTER_DOCUMENT200†A cluster definition/bundle was posted to /api/profiles/import; import it via /api/clusters/import instead (the flat profile format has no member/budget semantics)

† Handler-level refusal returned inside the {success:false, error} envelope with HTTP 200 — see the 200-envelope note below.

Circuit & sensing errors

Circuit serving spans several SAEs and carries an evidence rung; several of these are handler-level refusals returned in the envelope with HTTP 200 (see the note below) rather than HTTP error statuses, so the client can surface the rung/contention and re-send with an acknowledgement.

CodeHTTPMeaning
CIRCUIT_NOT_FOUND404No circuit with that ID
UNVALIDATED_CIRCUIT200†Activating a circuit below rung 2 (CAUSALLY_VALIDATED) without acknowledgement — re-send with acknowledge_unvalidated=true. The payload carries the evidence rung so the override is deliberate
CIRCUIT_LAYER_CONTENTION200†The circuit's layers are already served by another active circuit. details names the incumbent(s) and the measured hazard; overridable with allow_layer_overlap=true unless a same-key collision (colliding_keys present) makes it non-overridable
NO_ACTIVE_CIRCUIT200†An operation needing an active circuit (e.g. PUT /api/circuits/active/intensity) was called with none serving
AMBIGUOUS_ACTIVE_CIRCUIT200†Several circuits serve, so there is no single "active circuit" to dial — details.active_circuits lists them; deactivate all but one, or dial through the owning cluster
SAE_SET_INCOMPLETE422A circuit member's layer has no (unique) attached SAE (also listed under SAE & steering above)
CIRCUIT_SENSING_EVENT_NOT_FOUND404Circuit edge sensing event id that doesn't exist (pruned, cleared, or never existed)
SENSING_EVENT_NOT_FOUND404Cluster co-activation sensing event id that doesn't exist

† Handler-level refusal in the envelope with HTTP 200 — see below.

The 200-envelope house style

Most errors map to an HTTP error status. A few circuit refusals deliberately do not: UNVALIDATED_CIRCUIT, CIRCUIT_LAYER_CONTENTION, NO_ACTIVE_CIRCUIT, AMBIGUOUS_ACTIVE_CIRCUIT (and the profile import guards VALIDATION_ERROR / IS_CLUSTER_DOCUMENT) return HTTP 200 with the standard {success:false, data:null, error:{code, message, details}} envelope. These are decisions the handler makes about a well-formed request — an evidence-rung gate, a layer-contention gate, "no/ambiguous active circuit" — rather than malformed input or a missing resource. The code is still stable and machine-readable; clients must branch on success/error.code, not on the HTTP status, for these. The rich details (the rung, the incumbent, the measured hazard) is what lets the caller re-send with acknowledge_unvalidated or allow_layer_overlap.

General

CodeHTTPMeaning
VALIDATION_ERROR422Request body failed schema validation (also FastAPI's native 422s). A few handlers also return this code in a 200 envelope for semantic input problems — see the 200-envelope note

Handling errors in code

r = requests.post(f"{MILLM}/api/saes/steering",
json={"feature_idx": 999999, "value": 40})
body = r.json()
if not body["success"]:
code = body["error"]["code"] # "INVALID_FEATURE_INDEX"
details = body["error"]["details"] # {"feature_idx": 999999, "d_sae": 16384}

For /v1 endpoints, OpenAI SDKs raise their native exceptions (NotFoundError, RateLimitError, …) based on the HTTP status.