pub enum CoreError {
Show 36 variants
Internal(String),
ConfirmationRequired {
operation: String,
},
InvalidImportFile {
reason: String,
},
ImportDenied {
project: String,
problem: String,
endpoint: Option<String>,
},
InvalidInput {
reason: String,
},
ProfileNotFound {
name: String,
known: Vec<String>,
},
NoActiveProfile,
SecretUnavailable {
profile: String,
},
ConfigInvalid {
reason: String,
},
PollIntervalTooSmall {
profile: String,
},
Network {
url: String,
source: Option<Error>,
observation: Option<String>,
},
Auth {
status: u16,
endpoint: Option<String>,
},
GatewayTooOld {
found: String,
minimum: String,
endpoint: Option<String>,
},
GatewayNotCommissioned {
endpoint: Option<String>,
},
GatewayRestarting {
endpoint: Option<String>,
},
NotFound {
endpoint: Option<String>,
},
ProjectExists {
name: String,
endpoint: Option<String>,
},
ResourceBinary {
path: String,
endpoint: Option<String>,
},
TrialNotExpired {
remaining_s: i64,
endpoint: Option<String>,
},
RoutesNotDeployed {
project: String,
route: String,
endpoint: Option<String>,
},
WebdevUnlicensed {
endpoint: Option<String>,
},
RouteVersionMismatch {
route: String,
deployed: String,
expected: String,
endpoint: Option<String>,
},
ProviderNotFound {
name: String,
endpoint: Option<String>,
},
WebdevRouteError {
code: String,
message: String,
endpoint: Option<String>,
},
TagCollision {
provider: String,
names: Vec<String>,
endpoint: Option<String>,
},
AlarmJournalMissing {
endpoint: Option<String>,
},
SessionNotPrunable {
id: String,
endpoint: Option<String>,
},
Rig(String),
EamNotController {
endpoint: Option<String>,
},
ProviderRootUnsupported {
endpoint: Option<String>,
},
EamTaskTypeRefused {
task_type: String,
},
EamTaskInFlight {
task: String,
detail: String,
endpoint: Option<String>,
},
ScriptExecNotConfigured {
profile: String,
},
LintToolAbsent,
GatewayClientError {
status: u16,
endpoint: String,
body: String,
},
BundleNotAvailable {
state: String,
},
}Expand description
Every failure ign can report. One variant per contract class; code(),
exit_code(), hint() are total functions over it.
Variants§
Internal(String)
Unexpected runtime failure — the catch-all; report as a bug. Exit 1.
ConfirmationRequired
Destructive operation invoked without --yes. Exit 2 (same class as
usage: it names a flag the caller must add; clap renders its own
usage errors with its exit 2 — never hook clap).
InvalidImportFile
An import byte source the caller must fix (wrong file, too big,
unreadable). Exit 2 — usage class: it names what the CALLER must
change, like Self::ConfirmationRequired (03-02).
ImportDenied
The gateway ANSWERED the import POST with HTTP 200 but the
body says {"success": false, "problem": "…"} — the
denial-rides-200 class the WebDev family handles (05-01)
applied to the project-import family (05-07, UAT Gap 1):
without this check the import caller reports ok while nothing
landed. Exit 6 — target state: the gateway refused the
import (the problem text names why, verbatim).
Fields
InvalidInput
A command input the caller must fix (unreadable --file, failed
stdin read). Exit 2 — usage class, the generic sibling of
Self::InvalidImportFile (03-03: resource put’s byte
source).
ProfileNotFound
Named profile absent from config. Exit 3.
NoActiveProfile
No --profile, no IGNITION_PROFILE, no active profile in config.
Exit 3. Constructible from the CLI once config resolution lands
(01-03); the taxonomy is complete on day one.
No credential resolvable for the profile (env, token_env, keyring all missed or failed). Exit 3.
ConfigInvalid
Config file unreadable or wrong shape. Exit 3.
PollIntervalTooSmall
A profile’s poll_interval_secs is below the 1-second floor
(08-01, TUIX-05 clamp): sub-second gateway polling is refused at
load time — the TUI’s background refresh cadence may not hammer
the gateway. Exit 3 — the CONFIG class (the Phase-7 additive-slug
precedent, e.g. eam_not_controller: same exit class, own slug,
never a new exit code — the 1–7 taxonomy is frozen).
Network
Gateway unreachable / timeout / TLS failure. Exit 4.
source: None marks a POLL deadline expiry (02-04 poll.rs):
same class, same slug (network_error), no new variant — the
transport-error source a real failure carries is simply
absent, and url describes what was being waited on instead
(the poll’s subject). The deadline’s last observation rides
observation (09-07): when it is Some the gateway ANSWERED
with a concrete state, and the message leads with “no terminal
state” — it NEVER claims unreachability for an observed
answer; None keeps the plain unreachability wording.
Fields
Auth
Gateway reachable but rejected credentials (401/403). Exit 5.
GatewayTooOld
Gateway reachable but the command is invalid for its current state — version below minimum, uncommissioned, mid-restart, or a missing resource. Exit 6.
Fields
GatewayNotCommissioned
Gateway reachable but uncommissioned — every /data route 302s to
/welcome (verified on a fresh 8.3.6 container; 02-RESEARCH
§Error-Body Sniffing). Exit 6.
GatewayRestarting
Gateway restarting — webserver answers (503) but services are down (verified restart lifecycle: webserver never drops the connection). Exit 6.
NotFound
Named resource absent (404) — terminating a nonexistent session id,
an unknown path, or a pre-8.3 gateway’s JSON
{"message": "No route match for path: …"}. Exit 6.
ProjectExists
A project of this name already exists and the import’s collision policy is abort — the CLI-side pre-check refused BEFORE any upload (the server’s own answer remains the backstop). Exit 6 — target state: the command is invalid for the gateway’s current state (03-02, the GatewayTooOld action-built-variant precedent: constructed by the actions layer, not classify).
Fields
ResourceBinary
A binary (data.bin-class) resource met the surgical JSON/text loop — REFUSED rather than corrupted through it. Exit 6 — target state: the command is invalid for that resource’s nature; the export/import family owns binary resources (Pitfall 7).
Fields
TrialNotExpired
The gateway refuses trial resets while the trial is still active — live-discovered on 8.3.3 during 04-03’s spike: the reset POST answers 403 on a NON-expired trial (verified from the browser page itself with the exact UI headers), and 200 + the flip on an expired one. The action layer’s expiry pre-check turns that misleading auth-shaped 403 into the honest target-state refusal. Exit 6 (the ProjectExists precedent: action-constructed, not classify).
Fields
RoutesNotDeployed
The WebDev route family a command depends on is not deployed —
the presence probe answered 405, the live-proven 8.3 absent
marker (missing routes AND missing projects both answer 405,
NOT 404; 05-RESEARCH Pitfall 1). Exit 6 — target state: the
command is invalid until ign webdev deploy installs the
routes (the TrialNotExpired precedent: action-constructed, not
classify).
Fields
WebdevUnlicensed
The WebDev module answered 402 — installed but unlicensed (a
trial-expired gateway; live-verified cross-version on 8.3.6,
05-RESEARCH §Servlet). Exit 6 — no /system/webdev route can
answer until the gateway is licensed.
RouteVersionMismatch
A deployed route’s handshake version differs from the embedded bundle’s — the CLI refuses rather than auto-upgrading either side (roadmap-locked: actionable error, no auto-upgrade magic). Exit 6.
Fields
expected: StringThe embedded bundle’s version
(crate::webdev::ROUTE_BUNDLE_VERSION).
ProviderNotFound
A tag provider of this name does not exist — the find→signature→delete chain’s find half missed (05-04, TAGS-01). Exit 6 — target state: the named thing is absent (the ProjectExists precedent family: action-constructed, not classify — the honest, family-specific refusal over a bare 404).
Fields
WebdevRouteError
A WebDev route answered HTTP 200 with a body denial
({ok:false, error{code,message}}) whose machine code this CLI
does not specifically map — code + message ride verbatim so
agents can branch on the stable route contract (05-01). Exit 6
— target state: the deployed route refused the action.
Fields
TagCollision
A tag import under abort policy found EXISTING tags at the target provider (05-05, TAGS-09) — the browse pre-check refuses BEFORE any route write (the LOCKED Phase-3 collision matrix mapped onto configure’s ‘a’/‘o’). Exit 6 — target state: the named tags exist; overwrite is the explicit, guarded opt-in.
Fields
AlarmJournalMissing
The gateway has no alarm-journal profile configured — alarm
history has nowhere to read from. The alarms route’s
structured no_alarm_journal denial maps here (the
denial_to_error seam, 05-06 TAGS-07): DEFAULT rigs hit this
ALWAYS, because the journal is a config-resource chain —
database connection + ignition/alarm-journal profile + the
general-alarm-settings singleton pointing at it. Exit 6 —
target state: the command is invalid for the gateway’s
current state until that chain is provisioned (the honest,
actionable refusal over a bare route error).
SessionNotPrunable
Pruning a LIVE Designer session entry — the gateway’s prune route answers 409 (empty body, wire-verified on 8.3.3, 06-UAT test 6): prune removes STALE entries only, so the command is invalid while the Designer is still open. Exit 6 — target state (additive slug in the frozen taxonomy’s established growth pattern; classify()’s ROUTE-SCOPED 409 arm constructs this — the first classify-constructed refusal added since the 02-01 set).
Fields
Rig(String)
Docker/compose rig failure. Exit 7. Reserved — first used in Phase 4; trivially constructible so the taxonomy enumerates completely today.
EamNotController
The gateway’s EAM module is not configured as a controller —
every /data/eam/api/v1/* runtime endpoint answers 403 with
“This operation can only be performed when EAM is configured
as a controller” on a stock gateway (live-proven 8.3.3,
07-RESEARCH): a STATE refusal, not auth — the token is fine,
the module’s role is not. Message-classified at the classify
seam, path-scoped to /data/eam/ so generic 403s elsewhere
cannot shift (the trial_not_expired pattern, classify
edition). Exit 6 — target state.
ProviderRootUnsupported
A provider-ROOT tag path ([default] alone, or a bare first
segment that resolves to a provider) met the tagConfig route
— system.tag.getConfiguration/exportTags need an RPC
context WebDev threads don’t carry (live-proven 8.3.3
b2026012009, both gateways; 07-UAT test 12). The route
refuses honestly (pre-call bracket detection + RpcContext
translation for the bare form) instead of surfacing the
IllegalStateException as a generic route error; subtree
paths ([default]folder) are the supported form. Exit 6 —
target state: a platform limitation, not a bug.
EamTaskTypeRefused
An eam task new whose type is in the REFUSED set —
eam_restoreBackup, eam_installModules, eam_remoteUpgrade
are fleet-destructive (they push backups/modules/upgrades to
every AGENT target), and the CLI refuses them outright over
guard-everything: honest refusal with the v2 scope pointer
(the planner-locked create ladder’s top rung). Exit 6 —
target state (additive slug alongside Task 2’s).
EamTaskInFlight
An eam task force whose slot a leftover run occupies — the
force route answers 409 with the gateway’s own Jetty page
(“Task ‘X (forced)’ already exists! It must be completed or
deleted before another task of this type can be force
executed.”; live-captured 8.3.3, 07-UAT test 7). Exit 6 —
target state (the session_not_prunable precedent,
force-route edition): the gateway’s state refused the command,
not a bug; classify()’s path-scoped 409 arm constructs this.
Fields
ScriptExecNotConfigured
The scriptExec route is not configured for this profile — no
webdev secret is persisted, which can only mean ign webdev deploy --with-script-exec has never run (deploy persists the
secret 0600 BEFORE upload, so a deployed route without a
stored secret is not a reachable state). ign script run’s
opt-in is STRUCTURAL — the deploy flag — so the verb carries
no --yes guard and refuses here instead (07-03, SCRPT-01:
the TrialNotExpired precedent — action-constructed, not
classify). Exit 6 — target state.
LintToolAbsent
ign lint found no ignition-lint executable on PATH — the
delegation has nothing to delegate to (07-04, INTR-02). The
hint carries the install command + repo. Exit 6 — target
state (additive slug; the environment lacks the tool, the
command is fine).
GatewayClientError
The gateway answered the api call with a 4xx this CLI does not
curate — the caller’s request is the problem, and the body is
theirs to read. Exit 2 (usage class; additive slug — the
poll_interval_too_small precedent: same class, own slug, no new
exit code). Constructed ONLY by the api-call-scoped classify arm
(09-01: the api_call parameter) — a curated command’s 4xx keeps
its existing classification (the catch-all cannot fire without
the parameter). The body is the gateway’s answer VERBATIM,
truncated at GATEWAY_CLIENT_BODY_CAP_BYTES with the explicit
GATEWAY_CLIENT_BODY_TRUNCATION_MARKER via truncate_api_body
at construction.
Fields
body: StringThe response body VERBATIM, truncated at
GATEWAY_CLIENT_BODY_CAP_BYTES with an explicit marker.
BundleNotAvailable
No diagnostics bundle is available — the status poll ANSWERED
with a captured TERMINAL steady state meaning “no current
bundle” (Invalid: live-proven on 8.3.6 rig ign-p9-836,
2026-09-07 UAT / 09-UAT.md Gap 3 — a Valid bundle decays to
Invalid within ~2 minutes UNPROMPTED and stays Invalid;
only a fresh generate changes it, polling cannot). Exit 6 —
target state (the ImportDenied precedent: a gateway-answered
refusal riding its own class, action-constructed by bundle wait’s probe, not classify).
Implementations§
Source§impl CoreError
impl CoreError
Sourcepub fn exit_code(&self) -> u8
pub fn exit_code(&self) -> u8
The LOCKED exit-code mapping — the only place exit codes are decided.
Sourcepub fn endpoint(&self) -> Option<String>
pub fn endpoint(&self) -> Option<String>
URL/path of the request involved, when one was — populated for the network, auth, and target-state classes (CORE-05).
Sourcepub fn tui_tty_refusal() -> Self
pub fn tui_tty_refusal() -> Self
The ign tui TTY refusal (06-07): InvalidInput carrying the
ONE reason whose hint is terminal-contextual — see
TUI_TTY_REFUSAL_REASON. Same slug, same exit 2 (frozen
taxonomy; only the hint differs).
Sourcepub fn envelope<'a>(&self, profile: Option<&'a str>) -> ErrorEnvelope<'a>
pub fn envelope<'a>(&self, profile: Option<&'a str>) -> ErrorEnvelope<'a>
Build the LOCKED failure envelope for this error (field order is part
of the golden contract: ok, profile, error then code,
message, endpoint, hint).
Trait Implementations§
Source§impl Error for CoreError
impl Error for CoreError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()