Skip to main content

aube_codes/
errors.rs

1//! Error codes (`ERR_AUBE_*`).
2//!
3//! Each constant's *value* matches its identifier. The `ALL` slice
4//! is the registry — it gates the generated docs page
5//! (`docs/error-codes.md`, produced by the
6//! `generate-error-codes-docs` binary) and the self-tests in
7//! `lib.rs`. New codes go in both places: define a `pub const`,
8//! then add a [`crate::CodeMeta`] entry to `ALL` carrying the
9//! category, one-line description, and (optional) bespoke exit
10//! code.
11
12use crate::CodeMeta;
13
14// ── lockfile ─────────────────────────────────────────────────────────
15pub const ERR_AUBE_NO_LOCKFILE: &str = "ERR_AUBE_NO_LOCKFILE";
16pub const ERR_AUBE_LOCKFILE_PARSE: &str = "ERR_AUBE_LOCKFILE_PARSE";
17pub const ERR_AUBE_LOCKFILE_UNSUPPORTED_FORMAT: &str = "ERR_AUBE_LOCKFILE_UNSUPPORTED_FORMAT";
18pub const ERR_AUBE_RESOLUTION_SHAPE_MISMATCH: &str = "ERR_AUBE_RESOLUTION_SHAPE_MISMATCH";
19
20// ── resolver ─────────────────────────────────────────────────────────
21pub const ERR_AUBE_NO_MATCHING_VERSION: &str = "ERR_AUBE_NO_MATCHING_VERSION";
22pub const ERR_AUBE_NO_MATURE_MATCHING_VERSION: &str = "ERR_AUBE_NO_MATURE_MATCHING_VERSION";
23pub const ERR_AUBE_REGISTRY_ERROR: &str = "ERR_AUBE_REGISTRY_ERROR";
24pub const ERR_AUBE_UNKNOWN_CATALOG: &str = "ERR_AUBE_UNKNOWN_CATALOG";
25pub const ERR_AUBE_UNKNOWN_CATALOG_ENTRY: &str = "ERR_AUBE_UNKNOWN_CATALOG_ENTRY";
26pub const ERR_AUBE_BLOCKED_EXOTIC_SUBDEP: &str = "ERR_AUBE_BLOCKED_EXOTIC_SUBDEP";
27pub const ERR_AUBE_TRUST_DOWNGRADE: &str = "ERR_AUBE_TRUST_DOWNGRADE";
28pub const ERR_AUBE_TRUST_MISSING_TIME: &str = "ERR_AUBE_TRUST_MISSING_TIME";
29// `#[rustfmt::skip]` keeps the long names on a single visual line so the
30// declaration list reads as a flat table — rustfmt would otherwise wrap
31// to a `name: &str =\n    "name";` two-liner for any const past col 100.
32#[rustfmt::skip] pub const ERR_AUBE_TRUST_EXCLUDE_INVALID_VERSION_UNION: &str = "ERR_AUBE_TRUST_EXCLUDE_INVALID_VERSION_UNION";
33#[rustfmt::skip] pub const ERR_AUBE_TRUST_EXCLUDE_NAME_GLOB_WITH_VERSIONS: &str = "ERR_AUBE_TRUST_EXCLUDE_NAME_GLOB_WITH_VERSIONS";
34pub const ERR_AUBE_PEER_CONTEXT_NOT_CONVERGED: &str = "ERR_AUBE_PEER_CONTEXT_NOT_CONVERGED";
35
36// ── registry / network ──────────────────────────────────────────────
37pub const ERR_AUBE_PACKAGE_NOT_FOUND: &str = "ERR_AUBE_PACKAGE_NOT_FOUND";
38pub const ERR_AUBE_VERSION_NOT_FOUND: &str = "ERR_AUBE_VERSION_NOT_FOUND";
39pub const ERR_AUBE_UNAUTHORIZED: &str = "ERR_AUBE_UNAUTHORIZED";
40pub const ERR_AUBE_OFFLINE: &str = "ERR_AUBE_OFFLINE";
41pub const ERR_AUBE_INVALID_PACKAGE_NAME: &str = "ERR_AUBE_INVALID_PACKAGE_NAME";
42pub const ERR_AUBE_REGISTRY_WRITE_REJECTED: &str = "ERR_AUBE_REGISTRY_WRITE_REJECTED";
43pub const ERR_AUBE_MALICIOUS_PACKAGE: &str = "ERR_AUBE_MALICIOUS_PACKAGE";
44pub const ERR_AUBE_LOW_DOWNLOAD_PACKAGE: &str = "ERR_AUBE_LOW_DOWNLOAD_PACKAGE";
45pub const ERR_AUBE_ADVISORY_CHECK_FAILED: &str = "ERR_AUBE_ADVISORY_CHECK_FAILED";
46pub const ERR_AUBE_SECURITY_SCANNER_FATAL: &str = "ERR_AUBE_SECURITY_SCANNER_FATAL";
47pub const ERR_AUBE_SECURITY_SCANNER_FAILED: &str = "ERR_AUBE_SECURITY_SCANNER_FAILED";
48
49// ── tarball / store ─────────────────────────────────────────────────
50pub const ERR_AUBE_TARBALL_INTEGRITY: &str = "ERR_AUBE_TARBALL_INTEGRITY";
51pub const ERR_AUBE_TARBALL_EXTRACT: &str = "ERR_AUBE_TARBALL_EXTRACT";
52pub const ERR_AUBE_PKG_CONTENT_MISMATCH: &str = "ERR_AUBE_PKG_CONTENT_MISMATCH";
53pub const ERR_AUBE_TARBALL_URL_MISMATCH: &str = "ERR_AUBE_TARBALL_URL_MISMATCH";
54pub const ERR_AUBE_NO_HOME: &str = "ERR_AUBE_NO_HOME";
55pub const ERR_AUBE_GIT_ERROR: &str = "ERR_AUBE_GIT_ERROR";
56
57// ── linker ──────────────────────────────────────────────────────────
58pub const ERR_AUBE_LINK_FAILED: &str = "ERR_AUBE_LINK_FAILED";
59pub const ERR_AUBE_PATCH_FAILED: &str = "ERR_AUBE_PATCH_FAILED";
60pub const ERR_AUBE_MISSING_PACKAGE_INDEX: &str = "ERR_AUBE_MISSING_PACKAGE_INDEX";
61pub const ERR_AUBE_UNSAFE_INDEX_KEY: &str = "ERR_AUBE_UNSAFE_INDEX_KEY";
62pub const ERR_AUBE_UNSAFE_PACKAGE_NAME: &str = "ERR_AUBE_UNSAFE_PACKAGE_NAME";
63pub const ERR_AUBE_MISSING_STORE_FILE: &str = "ERR_AUBE_MISSING_STORE_FILE";
64
65// ── scripts ─────────────────────────────────────────────────────────
66pub const ERR_AUBE_SCRIPT_SPAWN: &str = "ERR_AUBE_SCRIPT_SPAWN";
67pub const ERR_AUBE_SCRIPT_NON_ZERO_EXIT: &str = "ERR_AUBE_SCRIPT_NON_ZERO_EXIT";
68#[rustfmt::skip] pub const ERR_AUBE_BUILD_POLICY_UNSUPPORTED_VALUE: &str = "ERR_AUBE_BUILD_POLICY_UNSUPPORTED_VALUE";
69#[rustfmt::skip] pub const ERR_AUBE_BUILD_POLICY_INVALID_VERSION_UNION: &str = "ERR_AUBE_BUILD_POLICY_INVALID_VERSION_UNION";
70#[rustfmt::skip] pub const ERR_AUBE_BUILD_POLICY_WILDCARD_WITH_VERSION: &str = "ERR_AUBE_BUILD_POLICY_WILDCARD_WITH_VERSION";
71
72// ── workspace / filter ──────────────────────────────────────────────
73pub const ERR_AUBE_WORKSPACE_PARSE: &str = "ERR_AUBE_WORKSPACE_PARSE";
74pub const ERR_AUBE_FILTER_EMPTY: &str = "ERR_AUBE_FILTER_EMPTY";
75pub const ERR_AUBE_FILTER_GIT_IO: &str = "ERR_AUBE_FILTER_GIT_IO";
76pub const ERR_AUBE_FILTER_GIT_FAILED: &str = "ERR_AUBE_FILTER_GIT_FAILED";
77
78// ── manifest ────────────────────────────────────────────────────────
79pub const ERR_AUBE_MANIFEST_PARSE: &str = "ERR_AUBE_MANIFEST_PARSE";
80pub const ERR_AUBE_MANIFEST_YAML_PARSE: &str = "ERR_AUBE_MANIFEST_YAML_PARSE";
81
82// ── engine / cli ────────────────────────────────────────────────────
83pub const ERR_AUBE_UNSUPPORTED_ENGINE: &str = "ERR_AUBE_UNSUPPORTED_ENGINE";
84pub const ERR_AUBE_RECURSIVE_NOT_SUPPORTED: &str = "ERR_AUBE_RECURSIVE_NOT_SUPPORTED";
85pub const ERR_AUBE_UNKNOWN_COMMAND: &str = "ERR_AUBE_UNKNOWN_COMMAND";
86pub const ERR_AUBE_NPM_ONLY_COMMAND: &str = "ERR_AUBE_NPM_ONLY_COMMAND";
87pub const ERR_AUBE_COMPLETION_FAILED: &str = "ERR_AUBE_COMPLETION_FAILED";
88pub const ERR_AUBE_REMOVE_PRIOR_INSTALL_DIR: &str = "ERR_AUBE_REMOVE_PRIOR_INSTALL_DIR";
89pub const ERR_AUBE_CONFIG_NESTED_AUBE_KEY: &str = "ERR_AUBE_CONFIG_NESTED_AUBE_KEY";
90pub const ERR_AUBE_CONFLICTING_BUILD_FLAGS: &str = "ERR_AUBE_CONFLICTING_BUILD_FLAGS";
91pub const ERR_AUBE_SHIM_CREATE_FAILED: &str = "ERR_AUBE_SHIM_CREATE_FAILED";
92pub const ERR_AUBE_SHIM_EXEC_FAILED: &str = "ERR_AUBE_SHIM_EXEC_FAILED";
93
94// ── node runtime (devEngines.runtime / .node-version / .nvmrc) ──────
95#[rustfmt::skip] pub const ERR_AUBE_RUNTIME_VERSION_UNSATISFIED: &str = "ERR_AUBE_RUNTIME_VERSION_UNSATISFIED";
96#[rustfmt::skip] pub const ERR_AUBE_RUNTIME_NO_MATCHING_VERSION: &str = "ERR_AUBE_RUNTIME_NO_MATCHING_VERSION";
97pub const ERR_AUBE_RUNTIME_DOWNLOAD_FAILED: &str = "ERR_AUBE_RUNTIME_DOWNLOAD_FAILED";
98#[rustfmt::skip] pub const ERR_AUBE_RUNTIME_CHECKSUM_MISMATCH: &str = "ERR_AUBE_RUNTIME_CHECKSUM_MISMATCH";
99pub const ERR_AUBE_RUNTIME_EXTRACT_FAILED: &str = "ERR_AUBE_RUNTIME_EXTRACT_FAILED";
100#[rustfmt::skip] pub const ERR_AUBE_RUNTIME_MISE_INSTALL_FAILED: &str = "ERR_AUBE_RUNTIME_MISE_INSTALL_FAILED";
101#[rustfmt::skip] pub const ERR_AUBE_RUNTIME_UNSUPPORTED_PLATFORM: &str = "ERR_AUBE_RUNTIME_UNSUPPORTED_PLATFORM";
102pub const ERR_AUBE_RUNTIME_IO: &str = "ERR_AUBE_RUNTIME_IO";
103
104// ── misc tracing::error! sites (non-fatal but high-severity) ────────
105pub const ERR_AUBE_PATCHES_TRACKING_WRITE: &str = "ERR_AUBE_PATCHES_TRACKING_WRITE";
106pub const ERR_AUBE_UNSAFE_SHEBANG_INTERPRETER: &str = "ERR_AUBE_UNSAFE_SHEBANG_INTERPRETER";
107
108/// Stable category labels that group codes in the generated docs and
109/// in `EXIT_TABLE`'s 10-wide allocation ranges. Public so the docs
110/// generator can iterate them in a deterministic order.
111pub mod category {
112    pub const LOCKFILE: &str = "Lockfile";
113    pub const RESOLVER: &str = "Resolver";
114    pub const TARBALL_STORE: &str = "Tarball / store";
115    pub const REGISTRY_NETWORK: &str = "Registry / network";
116    pub const SCRIPTS: &str = "Scripts / build";
117    pub const LINKER: &str = "Linker";
118    pub const MANIFEST_WORKSPACE: &str = "Manifest / workspace";
119    pub const ENGINE_CLI: &str = "Engine / CLI";
120    pub const MISC_SAFETY: &str = "Misc / safety";
121    /// Add-time / install-time supply-chain policy errors. Paired
122    /// with [`crate::warnings::category::SUPPLY_CHAIN`].
123    pub const SUPPLY_CHAIN: &str = "Supply chain (add-time)";
124}
125
126/// Registry of every error code with its category, description, and
127/// (optional) bespoke exit code. Walked by the
128/// `generate-error-codes-docs` binary and by the self-tests in
129/// `lib.rs` and `exit.rs`. New codes must be added here.
130pub const ALL: &[CodeMeta] = &[
131    // Lockfile
132    CodeMeta {
133        name: ERR_AUBE_NO_LOCKFILE,
134        category: category::LOCKFILE,
135        description: "An operation that required a lockfile (`--frozen-lockfile`, `aube fetch`, etc.) found none in the project.",
136        exit_code: Some(10),
137    },
138    CodeMeta {
139        name: ERR_AUBE_LOCKFILE_PARSE,
140        category: category::LOCKFILE,
141        description: "Lockfile is structurally invalid — version guard failed, YAML shape is wrong, or `yaml_serde` couldn't round-trip the contents.",
142        exit_code: Some(11),
143    },
144    CodeMeta {
145        name: ERR_AUBE_LOCKFILE_UNSUPPORTED_FORMAT,
146        category: category::LOCKFILE,
147        description: "Lockfile filename was recognized but its format isn't supported on this aube version.",
148        exit_code: Some(12),
149    },
150    CodeMeta {
151        name: ERR_AUBE_RESOLUTION_SHAPE_MISMATCH,
152        category: category::LOCKFILE,
153        description: "A registry-style lockfile dependency path is backed by a git, local directory, or direct tarball resolution.",
154        exit_code: Some(13),
155    },
156    // Resolver
157    CodeMeta {
158        name: ERR_AUBE_NO_MATCHING_VERSION,
159        category: category::RESOLVER,
160        description: "No published version of the named package satisfies the requested range.",
161        exit_code: Some(20),
162    },
163    CodeMeta {
164        name: ERR_AUBE_NO_MATURE_MATCHING_VERSION,
165        category: category::RESOLVER,
166        description: "A version satisfying the range exists but every candidate was younger than `minimumReleaseAge` and `minimumReleaseAgeStrict=true`.",
167        exit_code: Some(21),
168    },
169    CodeMeta {
170        name: ERR_AUBE_BLOCKED_EXOTIC_SUBDEP,
171        category: category::RESOLVER,
172        description: "Transitive dep used a `git:` / `file:` / `tarball` specifier and `blockExoticSubdeps=true`.",
173        exit_code: Some(22),
174    },
175    CodeMeta {
176        name: ERR_AUBE_TRUST_DOWNGRADE,
177        category: category::RESOLVER,
178        description: "Picked version dropped trust evidence the prior version had (`trustPolicy=no-downgrade`).",
179        exit_code: Some(23),
180    },
181    CodeMeta {
182        name: ERR_AUBE_TRUST_MISSING_TIME,
183        category: category::RESOLVER,
184        description: "Registry's packument has no `time` entry for the picked version (`trustPolicy=no-downgrade`).",
185        exit_code: Some(24),
186    },
187    CodeMeta {
188        name: ERR_AUBE_UNKNOWN_CATALOG,
189        category: category::RESOLVER,
190        description: "A `catalog:<name>` reference was used but the catalog isn't defined.",
191        exit_code: Some(25),
192    },
193    CodeMeta {
194        name: ERR_AUBE_UNKNOWN_CATALOG_ENTRY,
195        category: category::RESOLVER,
196        description: "The catalog exists but has no entry for the requested package.",
197        exit_code: Some(26),
198    },
199    CodeMeta {
200        name: ERR_AUBE_PEER_CONTEXT_NOT_CONVERGED,
201        category: category::RESOLVER,
202        description: "Peer-context fixed-point loop hit `MAX_ITERATIONS=16` without converging — usually mutually-recursive peers.",
203        exit_code: Some(27),
204    },
205    CodeMeta {
206        name: ERR_AUBE_REGISTRY_ERROR,
207        category: category::RESOLVER,
208        description: "Generic registry error from inside the resolver.",
209        exit_code: None,
210    },
211    CodeMeta {
212        name: ERR_AUBE_TRUST_EXCLUDE_INVALID_VERSION_UNION,
213        category: category::RESOLVER,
214        description: "A `trustPolicyExclude` pattern had a non-exact version.",
215        exit_code: None,
216    },
217    CodeMeta {
218        name: ERR_AUBE_TRUST_EXCLUDE_NAME_GLOB_WITH_VERSIONS,
219        category: category::RESOLVER,
220        description: "A `trustPolicyExclude` pattern combined a name glob with versions.",
221        exit_code: None,
222    },
223    // Tarball / store
224    CodeMeta {
225        name: ERR_AUBE_TARBALL_INTEGRITY,
226        category: category::TARBALL_STORE,
227        description: "Downloaded tarball's hash didn't match the lockfile's / packument's `dist.integrity`.",
228        exit_code: Some(30),
229    },
230    CodeMeta {
231        name: ERR_AUBE_TARBALL_EXTRACT,
232        category: category::TARBALL_STORE,
233        description: "Tarball couldn't be extracted (corrupt gzip, unexpected entry shape, etc.).",
234        exit_code: Some(31),
235    },
236    CodeMeta {
237        name: ERR_AUBE_PKG_CONTENT_MISMATCH,
238        category: category::TARBALL_STORE,
239        description: "Tarball's `package.json` declared a different `(name, version)` than the resolver expected (`strictStorePkgContentCheck=true`).",
240        exit_code: Some(32),
241    },
242    CodeMeta {
243        name: ERR_AUBE_GIT_ERROR,
244        category: category::TARBALL_STORE,
245        description: "Git operation failed during a `git:` dep prepare or checkout.",
246        exit_code: Some(33),
247    },
248    CodeMeta {
249        name: ERR_AUBE_TARBALL_URL_MISMATCH,
250        category: category::TARBALL_STORE,
251        description: "A registry lockfile entry's explicit tarball URL didn't match the registry metadata for that `(name, version)`.",
252        exit_code: Some(34),
253    },
254    CodeMeta {
255        name: ERR_AUBE_NO_HOME,
256        category: category::TARBALL_STORE,
257        description: "`HOME` (or platform equivalent) is unset, so aube can't locate its store.",
258        exit_code: None,
259    },
260    // Registry / network
261    CodeMeta {
262        name: ERR_AUBE_PACKAGE_NOT_FOUND,
263        category: category::REGISTRY_NETWORK,
264        description: "Registry returned 404 for the package name.",
265        exit_code: Some(40),
266    },
267    CodeMeta {
268        name: ERR_AUBE_VERSION_NOT_FOUND,
269        category: category::REGISTRY_NETWORK,
270        description: "Package exists but the requested version doesn't.",
271        exit_code: Some(41),
272    },
273    CodeMeta {
274        name: ERR_AUBE_UNAUTHORIZED,
275        category: category::REGISTRY_NETWORK,
276        description: "Registry returned 401/403 — missing or invalid auth. Run `aube login`.",
277        exit_code: Some(42),
278    },
279    CodeMeta {
280        name: ERR_AUBE_OFFLINE,
281        category: category::REGISTRY_NETWORK,
282        description: "Offline mode and the requested resource isn't in the local cache.",
283        exit_code: Some(43),
284    },
285    CodeMeta {
286        name: ERR_AUBE_INVALID_PACKAGE_NAME,
287        category: category::REGISTRY_NETWORK,
288        description: "A name doesn't match npm's grammar — rejected before any I/O so a hostile manifest can't use the cache-path builder as a write primitive.",
289        exit_code: Some(44),
290    },
291    CodeMeta {
292        name: ERR_AUBE_REGISTRY_WRITE_REJECTED,
293        category: category::REGISTRY_NETWORK,
294        description: "Registry rejected a publish/deprecate/owner write with a non-2xx response.",
295        exit_code: Some(45),
296    },
297    CodeMeta {
298        name: ERR_AUBE_MALICIOUS_PACKAGE,
299        category: category::REGISTRY_NETWORK,
300        description: "`aube add` refused a package because OSV reports it as malicious (`MAL-*` advisory). Hard block — confirmed-malicious advisories aren't a judgement call.",
301        exit_code: Some(46),
302    },
303    CodeMeta {
304        name: ERR_AUBE_LOW_DOWNLOAD_PACKAGE,
305        category: category::REGISTRY_NETWORK,
306        description: "`aube add` refused a package whose weekly downloads fall below `lowDownloadThreshold` in a non-interactive context (or when stdin is not a TTY). Pass `--allow-low-downloads` to bypass.",
307        exit_code: Some(47),
308    },
309    CodeMeta {
310        name: ERR_AUBE_ADVISORY_CHECK_FAILED,
311        category: category::REGISTRY_NETWORK,
312        description: "`aube add` couldn't reach the OSV advisory API and `advisoryCheck = required` is set. Distinct from `ERR_AUBE_MALICIOUS_PACKAGE` so CI tooling can tell a network outage from a confirmed malicious advisory.",
313        exit_code: Some(49),
314    },
315    CodeMeta {
316        name: ERR_AUBE_SECURITY_SCANNER_FATAL,
317        category: category::SUPPLY_CHAIN,
318        description: "User-configured `securityScanner` returned a `fatal`-level advisory against a package the user is trying to add. Bun-style pluggable scanner contract; the scanner itself decides what counts as fatal.",
319        exit_code: Some(48),
320    },
321    CodeMeta {
322        name: ERR_AUBE_SECURITY_SCANNER_FAILED,
323        category: category::SUPPLY_CHAIN,
324        description: "User-configured `securityScanner` couldn't be spawned, exited non-zero, timed out, or emitted unparseable JSON. Fail-closed by design: a configured scanner that can't run is treated as a refusal, not a free pass. Set `securityScanner = \"\"` to disable the integration when bootstrapping or recovering from a broken scanner.",
325        exit_code: None,
326    },
327    // Scripts / build
328    CodeMeta {
329        name: ERR_AUBE_SCRIPT_NON_ZERO_EXIT,
330        category: category::SCRIPTS,
331        description: "A lifecycle script (`preinstall` / `install` / `postinstall` / a `package.json` script) exited non-zero.",
332        exit_code: Some(50),
333    },
334    CodeMeta {
335        name: ERR_AUBE_SCRIPT_SPAWN,
336        category: category::SCRIPTS,
337        description: "Couldn't spawn a script's interpreter (shell missing, jail setup failed, etc.).",
338        exit_code: Some(51),
339    },
340    CodeMeta {
341        name: ERR_AUBE_BUILD_POLICY_UNSUPPORTED_VALUE,
342        category: category::SCRIPTS,
343        description: "An entry in `allowBuilds` had a value that wasn't `true`/`false`.",
344        exit_code: None,
345    },
346    CodeMeta {
347        name: ERR_AUBE_BUILD_POLICY_INVALID_VERSION_UNION,
348        category: category::SCRIPTS,
349        description: "An `allowBuilds` pattern's version union was unparseable.",
350        exit_code: None,
351    },
352    CodeMeta {
353        name: ERR_AUBE_BUILD_POLICY_WILDCARD_WITH_VERSION,
354        category: category::SCRIPTS,
355        description: "An `allowBuilds` pattern combined a wildcard name with a version union.",
356        exit_code: None,
357    },
358    // Linker
359    CodeMeta {
360        name: ERR_AUBE_PATCH_FAILED,
361        category: category::LINKER,
362        description: "Applying a `pnpm.patchedDependencies` patch failed.",
363        exit_code: Some(60),
364    },
365    CodeMeta {
366        name: ERR_AUBE_LINK_FAILED,
367        category: category::LINKER,
368        description: "Symlink / junction / hardlink couldn't be created — usually permissions or filesystem support.",
369        exit_code: Some(61),
370    },
371    CodeMeta {
372        name: ERR_AUBE_MISSING_PACKAGE_INDEX,
373        category: category::LINKER,
374        description: "Internal: a caller skipped `load_index` but the package wasn't already materialized.",
375        exit_code: Some(62),
376    },
377    CodeMeta {
378        name: ERR_AUBE_MISSING_STORE_FILE,
379        category: category::LINKER,
380        description: "A package index references a CAS shard that doesn't exist on disk. Re-run install to re-fetch.",
381        exit_code: Some(63),
382    },
383    // Manifest / workspace
384    CodeMeta {
385        name: ERR_AUBE_MANIFEST_PARSE,
386        category: category::MANIFEST_WORKSPACE,
387        description: "A `package.json` had a syntax error. miette renders a pointer at the offending byte.",
388        exit_code: Some(70),
389    },
390    CodeMeta {
391        name: ERR_AUBE_WORKSPACE_PARSE,
392        category: category::MANIFEST_WORKSPACE,
393        description: "An `aube-workspace.yaml` / `pnpm-workspace.yaml` was structurally invalid.",
394        exit_code: Some(71),
395    },
396    CodeMeta {
397        name: ERR_AUBE_MANIFEST_YAML_PARSE,
398        category: category::MANIFEST_WORKSPACE,
399        description: "A workspace YAML helper file was structurally invalid (no source pointer available).",
400        exit_code: None,
401    },
402    CodeMeta {
403        name: ERR_AUBE_FILTER_EMPTY,
404        category: category::MANIFEST_WORKSPACE,
405        description: "`--filter` was passed an empty selector.",
406        exit_code: None,
407    },
408    CodeMeta {
409        name: ERR_AUBE_FILTER_GIT_IO,
410        category: category::MANIFEST_WORKSPACE,
411        description: "A `--filter ...[ref]` selector failed to spawn `git`.",
412        exit_code: None,
413    },
414    CodeMeta {
415        name: ERR_AUBE_FILTER_GIT_FAILED,
416        category: category::MANIFEST_WORKSPACE,
417        description: "The git subprocess for a `--filter ...[ref]` selector exited non-zero.",
418        exit_code: None,
419    },
420    // Engine / CLI
421    CodeMeta {
422        name: ERR_AUBE_UNSUPPORTED_ENGINE,
423        category: category::ENGINE_CLI,
424        description: "One or more packages declared an `engines` constraint incompatible with the running Node/aube and `engine-strict=true`.",
425        exit_code: Some(80),
426    },
427    CodeMeta {
428        name: ERR_AUBE_UNKNOWN_COMMAND,
429        category: category::ENGINE_CLI,
430        description: "The named subcommand isn't a built-in aube command and isn't a script in the manifest.",
431        exit_code: Some(81),
432    },
433    CodeMeta {
434        name: ERR_AUBE_NPM_ONLY_COMMAND,
435        category: category::ENGINE_CLI,
436        description: "The user invoked an npm-only command (`whoami`, `token`, `owner`, `search`, `pkg`, `set-script`) — aube doesn't implement these; use npm.",
437        exit_code: Some(82),
438    },
439    CodeMeta {
440        name: ERR_AUBE_RECURSIVE_NOT_SUPPORTED,
441        category: category::ENGINE_CLI,
442        description: "A command was invoked under `--recursive` but doesn't support recursive execution.",
443        exit_code: None,
444    },
445    CodeMeta {
446        name: ERR_AUBE_COMPLETION_FAILED,
447        category: category::ENGINE_CLI,
448        description: "`aube completion` couldn't invoke `usage` to render the shell completions.",
449        exit_code: None,
450    },
451    CodeMeta {
452        name: ERR_AUBE_REMOVE_PRIOR_INSTALL_DIR,
453        category: category::ENGINE_CLI,
454        description: "Couldn't clean up a prior global install dir before re-installing.",
455        exit_code: None,
456    },
457    CodeMeta {
458        name: ERR_AUBE_CONFIG_NESTED_AUBE_KEY,
459        category: category::ENGINE_CLI,
460        description: "`aube config set <prefix>.<sub> …` was used for a key whose prefix is an aube map setting (e.g. `allowBuilds.<pkg>`). Such nested writes would otherwise land in `.npmrc` where aube doesn't read them and npm warns/errors about the unknown key — set the map in workspace yaml or `package.json#aube.<prefix>` instead.",
461        exit_code: None,
462    },
463    CodeMeta {
464        name: ERR_AUBE_CONFLICTING_BUILD_FLAGS,
465        category: category::ENGINE_CLI,
466        description: "`aube add` was passed the same package name in both `--allow-build` and `--deny-build`.",
467        exit_code: None,
468    },
469    CodeMeta {
470        name: ERR_AUBE_SHIM_CREATE_FAILED,
471        category: category::ENGINE_CLI,
472        description: "`aube activate <shell>` couldn't create or refresh an executable shim in aube's shim directory.",
473        exit_code: None,
474    },
475    CodeMeta {
476        name: ERR_AUBE_SHIM_EXEC_FAILED,
477        category: category::ENGINE_CLI,
478        description: "A runtime tool shim resolved its target but couldn't exec or spawn the selected tool.",
479        exit_code: None,
480    },
481    // Node runtime (devEngines.runtime / .node-version / .nvmrc)
482    CodeMeta {
483        name: ERR_AUBE_RUNTIME_VERSION_UNSATISFIED,
484        category: category::ENGINE_CLI,
485        description: "The project requires a Node.js version that isn't available and policy forbids fetching it (`devEngines.runtime` with `onFail: \"error\"`, `runtimeOnFail=error`, or offline mode with nothing installed).",
486        exit_code: Some(83),
487    },
488    CodeMeta {
489        name: ERR_AUBE_RUNTIME_NO_MATCHING_VERSION,
490        category: category::ENGINE_CLI,
491        description: "No Node.js release in the dist index satisfies the requested version (bad range, unknown LTS codename, or no build for this platform).",
492        exit_code: Some(84),
493    },
494    CodeMeta {
495        name: ERR_AUBE_RUNTIME_DOWNLOAD_FAILED,
496        category: category::ENGINE_CLI,
497        description: "Fetching the Node.js dist index, checksum file, or release archive failed after retries.",
498        exit_code: Some(85),
499    },
500    CodeMeta {
501        name: ERR_AUBE_RUNTIME_CHECKSUM_MISMATCH,
502        category: category::ENGINE_CLI,
503        description: "A downloaded Node.js archive's SHA-256 didn't match the lockfile pin or `SHASUMS256.txt`. The archive is discarded and never retried automatically.",
504        exit_code: Some(86),
505    },
506    CodeMeta {
507        name: ERR_AUBE_RUNTIME_EXTRACT_FAILED,
508        category: category::ENGINE_CLI,
509        description: "A Node.js release archive was corrupt or contained unsafe entry paths.",
510        exit_code: Some(87),
511    },
512    CodeMeta {
513        name: ERR_AUBE_RUNTIME_MISE_INSTALL_FAILED,
514        category: category::ENGINE_CLI,
515        description: "Delegating a Node.js install to mise failed — `mise install node@<version>` exited non-zero or the install wasn't discoverable afterwards. Fatal only under `runtimeInstaller=mise`; `auto` falls back to aube's own download.",
516        exit_code: Some(88),
517    },
518    CodeMeta {
519        name: ERR_AUBE_RUNTIME_UNSUPPORTED_PLATFORM,
520        category: category::ENGINE_CLI,
521        description: "No official Node.js build exists for this OS/architecture/libc. Set `nodeDownloadMirrors` to a mirror that carries one, or install Node via mise/system.",
522        exit_code: Some(89),
523    },
524    CodeMeta {
525        name: ERR_AUBE_RUNTIME_IO,
526        category: category::ENGINE_CLI,
527        description: "A filesystem operation in the runtime store failed (lock acquisition, staging, or publishing an install). Not a download failure — the message names the failing path.",
528        exit_code: None,
529    },
530    // Misc / safety
531    CodeMeta {
532        name: ERR_AUBE_UNSAFE_INDEX_KEY,
533        category: category::MISC_SAFETY,
534        description: "A package index key tried to escape its directory (path traversal defense in depth).",
535        exit_code: Some(90),
536    },
537    CodeMeta {
538        name: ERR_AUBE_UNSAFE_SHEBANG_INTERPRETER,
539        category: category::MISC_SAFETY,
540        description: "A `#!` shebang named an unsafe interpreter when generating a shim — substituted with `node` instead. Surfaced as `tracing::error!` but install continues.",
541        exit_code: Some(91),
542    },
543    CodeMeta {
544        name: ERR_AUBE_UNSAFE_PACKAGE_NAME,
545        category: category::MISC_SAFETY,
546        description: "A package/dependency alias would escape its `node_modules` slot if linked.",
547        exit_code: Some(92),
548    },
549    CodeMeta {
550        name: ERR_AUBE_PATCHES_TRACKING_WRITE,
551        category: category::MISC_SAFETY,
552        description: "Couldn't write `.aube-applied-patches.json` after applying patches. Non-fatal; next install may miss stale patched entries.",
553        exit_code: None,
554    },
555];