Skip to content
CLEARANCE
—
Error taxonomy

102 codes.
No bare errors.

An error is not a verdict. It means no verdict was produced — so every code carries a class, an exit code, the exact string a user will see, and a recovery path. A code that is not documented may not exist; a test fails the build if one does.

102
Codes, 10 domains
42
Fatal
36
Degrade
24
Warn / internal
Class → behaviour. The class decides everything.
ClassBehaviourExitAppears in the verdict?
FatalStop. No verdict is produced.2 / 3 / 4No — the process exits
DegradeContinue; record an undetermined[] or a warning0 (5 with --strict)Yes — visibly
WarnContinue; record a notes[] entry0Yes — visibly
InternalStop; print a reportable message, not a stack trace4No
Every DEGRADE and WARN appears in the verdict JSON. That is what makes an incomplete scan trustworthy: it is visibly incomplete.
E-CFG · 13 E-SCAN · 21 E-PARSE · 10 E-CORPUS · 12 E-POLICY · 10 E-RENDER · 7 E-NET · 8 E-AI · 12 E-MCP · 3 E-INT · 6

E-CFG

Configuration · 13 codes · exit 2

Every config error is fatal, because no intent means no answer. The tool refuses to guess — even helpfully.

CodeClassTrigger · exact user message
E-CFG-001FatalNo config found in the project root
No clearance.config.yml found in the project root.
E-CFG-002FatalRequired use.* fields missing
Config is missing required fields: use.commercial, use.distributed. Intent must be declared, not inferred.
E-CFG-003Fatallicence_model not an enum value
Invalid licence_model 'foo'. Expected one of: closed-source, open-source, source-available, dual, internal-only.
E-CFG-004FatalInvalid territory code
Invalid territory 'XX'. Use ISO-3166 alpha-2 or one of: EU, EEA, US, GB, APAC, global.
E-CFG-005FatalAn ignore rule has no reason
Policy ignore rule for 'vendor/legacy/**' has no reason. Every ignore must state why.
E-CFG-006FatalPolicy attempts a global severity downgrade
Policy cannot downgrade severity for all of <licence>. Policy may escalate or narrowly whitelist, never globally weaken.
E-CFG-007Fatalnever_allow contains an invalid SPDX id
'Foo-1.0' is not a valid SPDX identifier.
E-CFG-008FatalUnknown or future schema_version
Config schema_version 3 is not supported by this binary (max 1). Upgrade Clearance.
E-CFG-009WarnUser and project config disagree on a field
Project config overrides user config for 'use.commercial'.
E-CFG-010FatalIgnore targets the root or a lockfile
Cannot ignore the project root or a lockfile: 'package-lock.json'.
E-CFG-011FatalThe org policy file is invalid
The org policy file 'policy/org.yml' is invalid: unknown field 'severity_floor'.
E-CFG-012WarnA policy exception expired
The policy exception for 'vendor/legacy/**' expired on 2026-08-01 and no longer applies.
E-CFG-013WarnThe repository's own config enables AI egress — --no-ai overrides it
This repository's config enables AI egress (ai.enabled: true in 'clearance.config.yml'). Passing --no-ai overrides it.

E-SCAN

Scanner / filesystem · 21 codes

Only four of twenty-one are fatal, and each fatal case is "the tool genuinely cannot proceed". Skipping a hostile symlink is correct behaviour, not a failure — so it is a warning.

CodeClassTrigger · exact user message
E-SCAN-001Fatal 2Root does not exist or is not a directory
Path 'X' is not a directory.
E-SCAN-002Fatal 2Root is not readable
Cannot read 'X': permission denied.
E-SCAN-003DegradeFile count exceeds MaxFiles (200k)
Scan truncated: 200,000 of ~340,000 files. Results may be incomplete.
E-SCAN-004WarnSymlink resolves outside the project root (INV-4)
Skipped symlink 'X' → '/etc/passwd' (outside project root).
E-SCAN-005WarnSymlink cycle
Skipped symlink cycle at 'X'.
E-SCAN-006DegradeFile exceeds MaxFileSize (8 MiB)
Skipped 'X' (2.1 GB) — too large to parse. Recorded but not classified.
E-SCAN-007DegradePermission denied mid-walk
Cannot read 'X': permission denied. Skipped.
E-SCAN-008DegradeDepth exceeds MaxDepth (24)
Stopped descending at 'X' (depth > 24).
E-SCAN-009Fatal 2No manifest and no lockfile found
No dependency manifest found in 'X'. Point Clearance at a project root.
E-SCAN-010DegradeWeight file found, no licence resolvable
Weight file 'X' has no licence statement in config.json, README.md, MODEL_CARD.md or a sibling LICENSE.
E-SCAN-011DegradeVendored licence text unrecognised
Unrecognised licence text at 'X'. A human should read it.
E-SCAN-012DegradeWeight header truncated or unreadable
Cannot read the metadata header of 'X'.
E-SCAN-013DegradeTerms appear to live in a PDF
'X' references a PDF for its terms. Clearance cannot read PDFs. Read it yourself.
E-SCAN-014DegradeSource file exceeds the AST limit (2 MiB)
Skipped AST analysis of 'X' (3.4 MB). Falling back to string signals.
E-SCAN-015WarnLockfile newer than the manifest
'package-lock.json' is newer than 'package.json'. Using the lockfile.
E-SCAN-016WarnMultiple lockfiles for one ecosystem
Found both package-lock.json and pnpm-lock.yaml. Using pnpm-lock.yaml.
E-SCAN-017DegradeA vendored dependency contains a .env — existence recorded, contents never read
A vendored dependency contains a .env file. Clearance records its existence only; contents are never read.
E-SCAN-018WarnA file changed during the scan
'X' changed during the scan; results reflect the version read.
E-SCAN-019Fatal 2Scan timed out
Scan timed out after 60s.
E-SCAN-020DegradeAn ignore matched more than 50% of the tree
Ignore rule 'vendor/**' excluded 61% of files. This may be hiding dependencies.
E-SCAN-021WarnA spawned process name is not a literal
A process is spawned at 'scripts/serve.ts' with a command name that is not a literal. Clearance cannot tell which tool is invoked, so it records the spawn and nothing more.

E-PARSE

Parsing · 10 codes · all degrade except two

Refusing a hostile file and continuing is the right behaviour — the scan of the rest of the tree is still valid. Where a file is an attack pattern, the message says so, because a user needs to know that a weird lockfile is a security signal and not a typo.

CodeClassTrigger · exact user message
E-PARSE-001DegradeA lockfile is unparseable
Cannot parse 'package-lock.json': invalid JSON at line 42. Falling back to the manifest.
E-PARSE-002DegradeA manifest is unparseable
Cannot parse 'package.json': unexpected token at line 12.
E-PARSE-003Fatal 2Config YAML is invalid
Cannot parse clearance.config.yml: line 7: mapping values are not allowed here.
E-PARSE-004Fatal 2Config exceeds 256 KiB
clearance.config.yml is 512 KiB; the limit is 256 KiB.
E-PARSE-005DegradeA model-dir config.json is unparseable
Cannot parse 'models/config.json'. Skipping this licence source.
E-PARSE-006DegradeJSON nesting exceeds depth 64 — parser-attack guard, INV-10
'X' is nested too deeply (depth > 64). Refused to parse.
E-PARSE-007DegradeUnsupported SBOM specVersion
SBOM specVersion 1.2 is not supported (min 1.5). Falling back to a filesystem scan.
E-PARSE-008DegradeYAML anchor / alias bomb detected
Refused to parse 'X': excessive YAML aliases.
E-PARSE-009DegradeDisallowed tag or key
Refused to parse 'X': disallowed tag '!!python/object'.
E-PARSE-010DegradeUnsupported declared encoding
'X' declares charset 'utf-16'; only UTF-8 is supported.

E-CORPUS

Corpus · 12 codes · exit 3

The supply-chain guards. Both signature failures are fatal and both state that the previous corpus is unchanged — the user is never left with no corpus at all.

CodeClassTrigger · exact user message
E-CORPUS-001FatalCorpus file missing
Corpus not found at 'X'. Reinstall Clearance, or run 'clearance corpus update'.
E-CORPUS-002FatalSignature invalid or missing — the moat working
Corpus signature is invalid. Refusing to verdict. The previous corpus is unchanged.
E-CORPUS-003FatalCorpus schema version unsupported
Corpus schema v3 is not supported by this binary (max 1). Upgrade Clearance.
E-CORPUS-004DegradeOne entry fails schema validation
Corpus entry 'licence.foo' is invalid and was skipped.
E-CORPUS-005Fatal · buildDuplicate spdx_id — build-time only, never reaches a user
Fix the corpus source.
E-CORPUS-006FatalConfidence rose without a correction block — INV-8, confidence-laundering guard
Corpus entry 'licence.x' raised its confidence without a correction record. Refusing to load.
E-CORPUS-007Warn · buildCitation URL unreachable in the CI liveness check
Fix the URL or mark it stale.
E-CORPUS-008WarnA last_verified date is in the future
Corpus entry 'tos.x' has a future last_verified date. Treating it as LOW confidence.
E-CORPUS-009FatalEmbedded public key does not match the bundle
Corpus was signed with an unknown key. Refusing to load.
E-CORPUS-010DegradeA predicate references an unknown intent field
Corpus entry 'licence.x' references an unknown field. Entry skipped.
E-CORPUS-011WarnAn entry is past its staleness window
Corpus entry 'licence.x' was last verified on 2024-01-01, which is outside its 365-day staleness window. Its clauses are reported one confidence level lower.
E-CORPUS-012WarnNo previous corpus version to compare against — INV-8 runs in part
Loaded without a previous corpus version; confidence rises could not be compared. Supply the previous version to enforce INV-8 in full.

E-POLICY

Policy / verdict · 10 codes

The pattern is stark: only two of these can happen at runtime. Every other policy error is a corpus bug that a release guard should have caught before shipping.

CodeClassTrigger · exact user message
E-POLICY-001DegradeNo corpus entry for a dependency's SPDX id — the only routine runtime condition
No corpus entry for licence 'FOO-1.0' (npm:bar@1.0.0). The dependency is UNDETERMINED.
E-POLICY-002Internal 4A citation does not resolve
Internal error: citation 'x' did not resolve. This is a corpus bug.
E-POLICY-003Internal 4Predicate references an unknown field
Internal error: unknown field in predicate.
E-POLICY-004Internal 4Predicate type mismatch
Internal error: predicate type mismatch.
E-POLICY-005Internal 4Predicate recursion depth exceeded
Internal error: predicate too deeply nested.
E-POLICY-006Fatal 2A policy attempts a global downgrade
Policy cannot downgrade <licence>. Escalate or whitelist narrowly.
E-POLICY-007Internal 4A finding was built without a confidence — INV-2, should be impossible
Internal error: finding without confidence.
E-POLICY-008WarnTwo corpus entries disagree about the same licence
Corpus inconsistency for 'Apache-2.0': two obligations share an id.
E-POLICY-009DegradeAn obligation evaluates to UNKNOWN because intent is undeclared — the UNKNOWN path, made visible
Cannot determine 'scale.mau' — declare it in clearance.config.yml for a definitive verdict.
E-POLICY-010Internal 4The fold produced no result — a totality violation
Internal error: the verdict fold produced no result.

E-RENDER · E-NET · E-INT

The last twenty-one

Three domains with one idea each: a renderer failure is always a bug, a network failure is never fatal, and an internal code exists so a bug produces a triageable message instead of a stack trace.

E-RENDER · 7 · 6 fatal, 1 warn · exit 4 except 007 (exit 2)
001FatalOutput path not writable
002FatalJSON marshalling failed
003FatalSARIF schema validation failed
004FatalStatement renderer failed
005FatalSBOM renderer failed
006WarnColour requested, stdout not a TTY
007Fatal 2Unsupported output format
A renderer is a pure function. If it fails, it is a bug — there is no data condition that can make one fail.
E-NET · 8 · all degrade, exit 0
001DegradeCannot reach the corpus host
002DegradeTLS certificate invalid
003DegradeDownloaded corpus failed verification
004DegradeNetwork call attempted in --offline
005DegradeUpdate check timed out
006DegradeUnexpected outbound call blocked — INV-3 tripwire
007DegradeRedirect to a non-allowlisted host refused
008DegradeCorpus bundle exceeds 64 MiB
No network error is ever fatal. The local corpus always works — the product must work on a plane.
E-INT · 6 · 5 fatal exit 4, 1 warn
001FatalArchitectural import rule violated
002FatalRecovered from a panic
003FatalInvariant assertion failed
004FatalImpossible state reached
005FatalEmbedded corpus public key missing
006WarnBinary and corpus versions incompatible
These are bugs. Every one should be unreachable in a released binary, and CI guard tests exist to keep them that way.

E-AI · E-MCP

The fifteen that only exist once the network is armed

Both domains are off by default. No AI call is possible until --ai arms it, and the MCP server runs only when you start it. Every code in these two domains marks a boundary being tested, not a routine outcome.

E-AI · 12 · 7 fatal exit 2, 5 degrade exit 0
001Fatal 2No API key for the provider
002Fatal 2The provider rejected the key (HTTP 401)
003DegradeThe provider rate-limited the request (HTTP 429)
004DegradeCannot reach the provider
005DegradeAn AI response failed validation and was discarded
006DegradeAn AI suggestion would have changed a finding — discarded, INV-1 tripwire
007Fatal 2Unknown AI provider
008Fatal 2The key file is readable by other users (mode is not 600)
009Fatal 2base_url set on a provider that is not openai-compatible
010DegradeAn AI response exceeded the size cap and was discarded
011Fatal 2A field the provider requires was not supplied
012Fatal 2--ai and --offline passed together
Five of these degrade the explanation and leave the verdict untouched. A model may explain a verdict; it may never change one.
E-MCP · 3 · 1 fatal exit 2, 2 warn exit 0
001Fatal 2A request asked for a path outside the declared workspace root
002WarnThe client called a tool this server does not expose
003WarnAn MCP message exceeded the frame cap and was skipped
The server reads only inside the root it was started in. A refusal is answered with a JSON-RPC error and the server keeps running.

★

Of the 102, three carry the product's integrity

If these three behave correctly, the product is safe to ship even if the other ninety-nine codes are imperfect.

E-NET-006Degrade

Unexpected outbound call

If this ever fires in the wild, the privacy promise is broken. It is the runtime tripwire for INV-3 and it should never fire in a correct build.

E-CORPUS-002Fatal

Invalid corpus signature

If this fires, someone tried to poison the corpus. The refusal is the moat working — and the previous corpus is left untouched.

E-POLICY-001Degrade

No corpus entry for a licence

The only routine runtime condition, and it produces UNDETERMINED rather than a guess. This is INV-7 in practice.

The discipline that keeps 102 codes honest
RuleEnforcement
Every code has a constant in internal/cerrTestEveryErrorCodeIsDocumented
Every code appears in this filethe same test, reversed
No errors.New or fmt.Errorf without a codecustom linter
Codes are never renumbered or reuseda frozen range per domain
Every DEGRADE code has a fixture that triggers itTestEveryDegradeCodeHasFixture
No unexpected egress, everTestNoUnexpectedEgress