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";
18
19// ── resolver ─────────────────────────────────────────────────────────
20pub const ERR_AUBE_NO_MATCHING_VERSION: &str = "ERR_AUBE_NO_MATCHING_VERSION";
21pub const ERR_AUBE_NO_MATURE_MATCHING_VERSION: &str = "ERR_AUBE_NO_MATURE_MATCHING_VERSION";
22pub const ERR_AUBE_REGISTRY_ERROR: &str = "ERR_AUBE_REGISTRY_ERROR";
23pub const ERR_AUBE_UNKNOWN_CATALOG: &str = "ERR_AUBE_UNKNOWN_CATALOG";
24pub const ERR_AUBE_UNKNOWN_CATALOG_ENTRY: &str = "ERR_AUBE_UNKNOWN_CATALOG_ENTRY";
25pub const ERR_AUBE_BLOCKED_EXOTIC_SUBDEP: &str = "ERR_AUBE_BLOCKED_EXOTIC_SUBDEP";
26pub const ERR_AUBE_TRUST_DOWNGRADE: &str = "ERR_AUBE_TRUST_DOWNGRADE";
27pub const ERR_AUBE_TRUST_MISSING_TIME: &str = "ERR_AUBE_TRUST_MISSING_TIME";
28// `#[rustfmt::skip]` keeps the long names on a single visual line so the
29// declaration list reads as a flat table — rustfmt would otherwise wrap
30// to a `name: &str =\n    "name";` two-liner for any const past col 100.
31#[rustfmt::skip] pub const ERR_AUBE_TRUST_EXCLUDE_INVALID_VERSION_UNION: &str = "ERR_AUBE_TRUST_EXCLUDE_INVALID_VERSION_UNION";
32#[rustfmt::skip] pub const ERR_AUBE_TRUST_EXCLUDE_NAME_GLOB_WITH_VERSIONS: &str = "ERR_AUBE_TRUST_EXCLUDE_NAME_GLOB_WITH_VERSIONS";
33pub const ERR_AUBE_PEER_CONTEXT_NOT_CONVERGED: &str = "ERR_AUBE_PEER_CONTEXT_NOT_CONVERGED";
34
35// ── registry / network ──────────────────────────────────────────────
36pub const ERR_AUBE_PACKAGE_NOT_FOUND: &str = "ERR_AUBE_PACKAGE_NOT_FOUND";
37pub const ERR_AUBE_VERSION_NOT_FOUND: &str = "ERR_AUBE_VERSION_NOT_FOUND";
38pub const ERR_AUBE_UNAUTHORIZED: &str = "ERR_AUBE_UNAUTHORIZED";
39pub const ERR_AUBE_OFFLINE: &str = "ERR_AUBE_OFFLINE";
40pub const ERR_AUBE_INVALID_PACKAGE_NAME: &str = "ERR_AUBE_INVALID_PACKAGE_NAME";
41pub const ERR_AUBE_REGISTRY_WRITE_REJECTED: &str = "ERR_AUBE_REGISTRY_WRITE_REJECTED";
42pub const ERR_AUBE_MALICIOUS_PACKAGE: &str = "ERR_AUBE_MALICIOUS_PACKAGE";
43pub const ERR_AUBE_LOW_DOWNLOAD_PACKAGE: &str = "ERR_AUBE_LOW_DOWNLOAD_PACKAGE";
44pub const ERR_AUBE_ADVISORY_CHECK_FAILED: &str = "ERR_AUBE_ADVISORY_CHECK_FAILED";
45pub const ERR_AUBE_SECURITY_SCANNER_FATAL: &str = "ERR_AUBE_SECURITY_SCANNER_FATAL";
46pub const ERR_AUBE_SECURITY_SCANNER_FAILED: &str = "ERR_AUBE_SECURITY_SCANNER_FAILED";
47
48// ── tarball / store ─────────────────────────────────────────────────
49pub const ERR_AUBE_TARBALL_INTEGRITY: &str = "ERR_AUBE_TARBALL_INTEGRITY";
50pub const ERR_AUBE_TARBALL_EXTRACT: &str = "ERR_AUBE_TARBALL_EXTRACT";
51pub const ERR_AUBE_PKG_CONTENT_MISMATCH: &str = "ERR_AUBE_PKG_CONTENT_MISMATCH";
52pub const ERR_AUBE_NO_HOME: &str = "ERR_AUBE_NO_HOME";
53pub const ERR_AUBE_GIT_ERROR: &str = "ERR_AUBE_GIT_ERROR";
54
55// ── linker ──────────────────────────────────────────────────────────
56pub const ERR_AUBE_LINK_FAILED: &str = "ERR_AUBE_LINK_FAILED";
57pub const ERR_AUBE_PATCH_FAILED: &str = "ERR_AUBE_PATCH_FAILED";
58pub const ERR_AUBE_MISSING_PACKAGE_INDEX: &str = "ERR_AUBE_MISSING_PACKAGE_INDEX";
59pub const ERR_AUBE_UNSAFE_INDEX_KEY: &str = "ERR_AUBE_UNSAFE_INDEX_KEY";
60pub const ERR_AUBE_MISSING_STORE_FILE: &str = "ERR_AUBE_MISSING_STORE_FILE";
61
62// ── scripts ─────────────────────────────────────────────────────────
63pub const ERR_AUBE_SCRIPT_SPAWN: &str = "ERR_AUBE_SCRIPT_SPAWN";
64pub const ERR_AUBE_SCRIPT_NON_ZERO_EXIT: &str = "ERR_AUBE_SCRIPT_NON_ZERO_EXIT";
65#[rustfmt::skip] pub const ERR_AUBE_BUILD_POLICY_UNSUPPORTED_VALUE: &str = "ERR_AUBE_BUILD_POLICY_UNSUPPORTED_VALUE";
66#[rustfmt::skip] pub const ERR_AUBE_BUILD_POLICY_INVALID_VERSION_UNION: &str = "ERR_AUBE_BUILD_POLICY_INVALID_VERSION_UNION";
67#[rustfmt::skip] pub const ERR_AUBE_BUILD_POLICY_WILDCARD_WITH_VERSION: &str = "ERR_AUBE_BUILD_POLICY_WILDCARD_WITH_VERSION";
68
69// ── workspace / filter ──────────────────────────────────────────────
70pub const ERR_AUBE_WORKSPACE_PARSE: &str = "ERR_AUBE_WORKSPACE_PARSE";
71pub const ERR_AUBE_FILTER_EMPTY: &str = "ERR_AUBE_FILTER_EMPTY";
72pub const ERR_AUBE_FILTER_GIT_IO: &str = "ERR_AUBE_FILTER_GIT_IO";
73pub const ERR_AUBE_FILTER_GIT_FAILED: &str = "ERR_AUBE_FILTER_GIT_FAILED";
74
75// ── manifest ────────────────────────────────────────────────────────
76pub const ERR_AUBE_MANIFEST_PARSE: &str = "ERR_AUBE_MANIFEST_PARSE";
77pub const ERR_AUBE_MANIFEST_YAML_PARSE: &str = "ERR_AUBE_MANIFEST_YAML_PARSE";
78
79// ── engine / cli ────────────────────────────────────────────────────
80pub const ERR_AUBE_UNSUPPORTED_ENGINE: &str = "ERR_AUBE_UNSUPPORTED_ENGINE";
81pub const ERR_AUBE_RECURSIVE_NOT_SUPPORTED: &str = "ERR_AUBE_RECURSIVE_NOT_SUPPORTED";
82pub const ERR_AUBE_UNKNOWN_COMMAND: &str = "ERR_AUBE_UNKNOWN_COMMAND";
83pub const ERR_AUBE_NPM_ONLY_COMMAND: &str = "ERR_AUBE_NPM_ONLY_COMMAND";
84pub const ERR_AUBE_COMPLETION_FAILED: &str = "ERR_AUBE_COMPLETION_FAILED";
85pub const ERR_AUBE_REMOVE_PRIOR_INSTALL_DIR: &str = "ERR_AUBE_REMOVE_PRIOR_INSTALL_DIR";
86pub const ERR_AUBE_CONFIG_NESTED_AUBE_KEY: &str = "ERR_AUBE_CONFIG_NESTED_AUBE_KEY";
87pub const ERR_AUBE_CONFLICTING_BUILD_FLAGS: &str = "ERR_AUBE_CONFLICTING_BUILD_FLAGS";
88
89// ── misc tracing::error! sites (non-fatal but high-severity) ────────
90pub const ERR_AUBE_PATCHES_TRACKING_WRITE: &str = "ERR_AUBE_PATCHES_TRACKING_WRITE";
91pub const ERR_AUBE_UNSAFE_SHEBANG_INTERPRETER: &str = "ERR_AUBE_UNSAFE_SHEBANG_INTERPRETER";
92
93/// Stable category labels that group codes in the generated docs and
94/// in `EXIT_TABLE`'s 10-wide allocation ranges. Public so the docs
95/// generator can iterate them in a deterministic order.
96pub mod category {
97    pub const LOCKFILE: &str = "Lockfile";
98    pub const RESOLVER: &str = "Resolver";
99    pub const TARBALL_STORE: &str = "Tarball / store";
100    pub const REGISTRY_NETWORK: &str = "Registry / network";
101    pub const SCRIPTS: &str = "Scripts / build";
102    pub const LINKER: &str = "Linker";
103    pub const MANIFEST_WORKSPACE: &str = "Manifest / workspace";
104    pub const ENGINE_CLI: &str = "Engine / CLI";
105    pub const MISC_SAFETY: &str = "Misc / safety";
106    /// Add-time / install-time supply-chain policy errors. Paired
107    /// with [`crate::warnings::category::SUPPLY_CHAIN`].
108    pub const SUPPLY_CHAIN: &str = "Supply chain (add-time)";
109}
110
111/// Registry of every error code with its category, description, and
112/// (optional) bespoke exit code. Walked by the
113/// `generate-error-codes-docs` binary and by the self-tests in
114/// `lib.rs` and `exit.rs`. New codes must be added here.
115pub const ALL: &[CodeMeta] = &[
116    // Lockfile
117    CodeMeta {
118        name: ERR_AUBE_NO_LOCKFILE,
119        category: category::LOCKFILE,
120        description: "An operation that required a lockfile (`--frozen-lockfile`, `aube fetch`, etc.) found none in the project.",
121        exit_code: Some(10),
122    },
123    CodeMeta {
124        name: ERR_AUBE_LOCKFILE_PARSE,
125        category: category::LOCKFILE,
126        description: "Lockfile is structurally invalid — version guard failed, YAML shape is wrong, or `yaml_serde` couldn't round-trip the contents.",
127        exit_code: Some(11),
128    },
129    CodeMeta {
130        name: ERR_AUBE_LOCKFILE_UNSUPPORTED_FORMAT,
131        category: category::LOCKFILE,
132        description: "Lockfile filename was recognized but its format isn't supported on this aube version.",
133        exit_code: Some(12),
134    },
135    // Resolver
136    CodeMeta {
137        name: ERR_AUBE_NO_MATCHING_VERSION,
138        category: category::RESOLVER,
139        description: "No published version of the named package satisfies the requested range.",
140        exit_code: Some(20),
141    },
142    CodeMeta {
143        name: ERR_AUBE_NO_MATURE_MATCHING_VERSION,
144        category: category::RESOLVER,
145        description: "A version satisfying the range exists but every candidate was younger than `minimumReleaseAge` and `minimumReleaseAgeStrict=true`.",
146        exit_code: Some(21),
147    },
148    CodeMeta {
149        name: ERR_AUBE_BLOCKED_EXOTIC_SUBDEP,
150        category: category::RESOLVER,
151        description: "Transitive dep used a `git:` / `file:` / `tarball` specifier and `blockExoticSubdeps=true`.",
152        exit_code: Some(22),
153    },
154    CodeMeta {
155        name: ERR_AUBE_TRUST_DOWNGRADE,
156        category: category::RESOLVER,
157        description: "Picked version dropped trust evidence the prior version had (`trustPolicy=no-downgrade`).",
158        exit_code: Some(23),
159    },
160    CodeMeta {
161        name: ERR_AUBE_TRUST_MISSING_TIME,
162        category: category::RESOLVER,
163        description: "Registry's packument has no `time` entry for the picked version (`trustPolicy=no-downgrade`).",
164        exit_code: Some(24),
165    },
166    CodeMeta {
167        name: ERR_AUBE_UNKNOWN_CATALOG,
168        category: category::RESOLVER,
169        description: "A `catalog:<name>` reference was used but the catalog isn't defined.",
170        exit_code: Some(25),
171    },
172    CodeMeta {
173        name: ERR_AUBE_UNKNOWN_CATALOG_ENTRY,
174        category: category::RESOLVER,
175        description: "The catalog exists but has no entry for the requested package.",
176        exit_code: Some(26),
177    },
178    CodeMeta {
179        name: ERR_AUBE_PEER_CONTEXT_NOT_CONVERGED,
180        category: category::RESOLVER,
181        description: "Peer-context fixed-point loop hit `MAX_ITERATIONS=16` without converging — usually mutually-recursive peers.",
182        exit_code: Some(27),
183    },
184    CodeMeta {
185        name: ERR_AUBE_REGISTRY_ERROR,
186        category: category::RESOLVER,
187        description: "Generic registry error from inside the resolver.",
188        exit_code: None,
189    },
190    CodeMeta {
191        name: ERR_AUBE_TRUST_EXCLUDE_INVALID_VERSION_UNION,
192        category: category::RESOLVER,
193        description: "A `trustPolicyExclude` pattern had a non-exact version.",
194        exit_code: None,
195    },
196    CodeMeta {
197        name: ERR_AUBE_TRUST_EXCLUDE_NAME_GLOB_WITH_VERSIONS,
198        category: category::RESOLVER,
199        description: "A `trustPolicyExclude` pattern combined a name glob with versions.",
200        exit_code: None,
201    },
202    // Tarball / store
203    CodeMeta {
204        name: ERR_AUBE_TARBALL_INTEGRITY,
205        category: category::TARBALL_STORE,
206        description: "Downloaded tarball's hash didn't match the lockfile's / packument's `dist.integrity`.",
207        exit_code: Some(30),
208    },
209    CodeMeta {
210        name: ERR_AUBE_TARBALL_EXTRACT,
211        category: category::TARBALL_STORE,
212        description: "Tarball couldn't be extracted (corrupt gzip, unexpected entry shape, etc.).",
213        exit_code: Some(31),
214    },
215    CodeMeta {
216        name: ERR_AUBE_PKG_CONTENT_MISMATCH,
217        category: category::TARBALL_STORE,
218        description: "Tarball's `package.json` declared a different `(name, version)` than the resolver expected (`strictStorePkgContentCheck=true`).",
219        exit_code: Some(32),
220    },
221    CodeMeta {
222        name: ERR_AUBE_GIT_ERROR,
223        category: category::TARBALL_STORE,
224        description: "Git operation failed during a `git:` dep prepare or checkout.",
225        exit_code: Some(33),
226    },
227    CodeMeta {
228        name: ERR_AUBE_NO_HOME,
229        category: category::TARBALL_STORE,
230        description: "`HOME` (or platform equivalent) is unset, so aube can't locate its store.",
231        exit_code: None,
232    },
233    // Registry / network
234    CodeMeta {
235        name: ERR_AUBE_PACKAGE_NOT_FOUND,
236        category: category::REGISTRY_NETWORK,
237        description: "Registry returned 404 for the package name.",
238        exit_code: Some(40),
239    },
240    CodeMeta {
241        name: ERR_AUBE_VERSION_NOT_FOUND,
242        category: category::REGISTRY_NETWORK,
243        description: "Package exists but the requested version doesn't.",
244        exit_code: Some(41),
245    },
246    CodeMeta {
247        name: ERR_AUBE_UNAUTHORIZED,
248        category: category::REGISTRY_NETWORK,
249        description: "Registry returned 401/403 — missing or invalid auth. Run `aube login`.",
250        exit_code: Some(42),
251    },
252    CodeMeta {
253        name: ERR_AUBE_OFFLINE,
254        category: category::REGISTRY_NETWORK,
255        description: "Offline mode and the requested resource isn't in the local cache.",
256        exit_code: Some(43),
257    },
258    CodeMeta {
259        name: ERR_AUBE_INVALID_PACKAGE_NAME,
260        category: category::REGISTRY_NETWORK,
261        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.",
262        exit_code: Some(44),
263    },
264    CodeMeta {
265        name: ERR_AUBE_REGISTRY_WRITE_REJECTED,
266        category: category::REGISTRY_NETWORK,
267        description: "Registry rejected a publish/deprecate/owner write with a non-2xx response.",
268        exit_code: Some(45),
269    },
270    CodeMeta {
271        name: ERR_AUBE_MALICIOUS_PACKAGE,
272        category: category::REGISTRY_NETWORK,
273        description: "`aube add` refused a package because OSV reports it as malicious (`MAL-*` advisory). Hard block — confirmed-malicious advisories aren't a judgement call.",
274        exit_code: Some(46),
275    },
276    CodeMeta {
277        name: ERR_AUBE_LOW_DOWNLOAD_PACKAGE,
278        category: category::REGISTRY_NETWORK,
279        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.",
280        exit_code: Some(47),
281    },
282    CodeMeta {
283        name: ERR_AUBE_ADVISORY_CHECK_FAILED,
284        category: category::REGISTRY_NETWORK,
285        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.",
286        exit_code: Some(49),
287    },
288    CodeMeta {
289        name: ERR_AUBE_SECURITY_SCANNER_FATAL,
290        category: category::SUPPLY_CHAIN,
291        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.",
292        exit_code: Some(48),
293    },
294    CodeMeta {
295        name: ERR_AUBE_SECURITY_SCANNER_FAILED,
296        category: category::SUPPLY_CHAIN,
297        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.",
298        exit_code: None,
299    },
300    // Scripts / build
301    CodeMeta {
302        name: ERR_AUBE_SCRIPT_NON_ZERO_EXIT,
303        category: category::SCRIPTS,
304        description: "A lifecycle script (`preinstall` / `install` / `postinstall` / a `package.json` script) exited non-zero.",
305        exit_code: Some(50),
306    },
307    CodeMeta {
308        name: ERR_AUBE_SCRIPT_SPAWN,
309        category: category::SCRIPTS,
310        description: "Couldn't spawn a script's interpreter (shell missing, jail setup failed, etc.).",
311        exit_code: Some(51),
312    },
313    CodeMeta {
314        name: ERR_AUBE_BUILD_POLICY_UNSUPPORTED_VALUE,
315        category: category::SCRIPTS,
316        description: "An entry in `allowBuilds` had a value that wasn't `true`/`false`.",
317        exit_code: None,
318    },
319    CodeMeta {
320        name: ERR_AUBE_BUILD_POLICY_INVALID_VERSION_UNION,
321        category: category::SCRIPTS,
322        description: "An `allowBuilds` pattern's version union was unparseable.",
323        exit_code: None,
324    },
325    CodeMeta {
326        name: ERR_AUBE_BUILD_POLICY_WILDCARD_WITH_VERSION,
327        category: category::SCRIPTS,
328        description: "An `allowBuilds` pattern combined a wildcard name with a version union.",
329        exit_code: None,
330    },
331    // Linker
332    CodeMeta {
333        name: ERR_AUBE_PATCH_FAILED,
334        category: category::LINKER,
335        description: "Applying a `pnpm.patchedDependencies` patch failed.",
336        exit_code: Some(60),
337    },
338    CodeMeta {
339        name: ERR_AUBE_LINK_FAILED,
340        category: category::LINKER,
341        description: "Symlink / junction / hardlink couldn't be created — usually permissions or filesystem support.",
342        exit_code: Some(61),
343    },
344    CodeMeta {
345        name: ERR_AUBE_MISSING_PACKAGE_INDEX,
346        category: category::LINKER,
347        description: "Internal: a caller skipped `load_index` but the package wasn't already materialized.",
348        exit_code: Some(62),
349    },
350    CodeMeta {
351        name: ERR_AUBE_MISSING_STORE_FILE,
352        category: category::LINKER,
353        description: "A package index references a CAS shard that doesn't exist on disk. Re-run install to re-fetch.",
354        exit_code: Some(63),
355    },
356    // Manifest / workspace
357    CodeMeta {
358        name: ERR_AUBE_MANIFEST_PARSE,
359        category: category::MANIFEST_WORKSPACE,
360        description: "A `package.json` had a syntax error. miette renders a pointer at the offending byte.",
361        exit_code: Some(70),
362    },
363    CodeMeta {
364        name: ERR_AUBE_WORKSPACE_PARSE,
365        category: category::MANIFEST_WORKSPACE,
366        description: "An `aube-workspace.yaml` / `pnpm-workspace.yaml` was structurally invalid.",
367        exit_code: Some(71),
368    },
369    CodeMeta {
370        name: ERR_AUBE_MANIFEST_YAML_PARSE,
371        category: category::MANIFEST_WORKSPACE,
372        description: "A workspace YAML helper file was structurally invalid (no source pointer available).",
373        exit_code: None,
374    },
375    CodeMeta {
376        name: ERR_AUBE_FILTER_EMPTY,
377        category: category::MANIFEST_WORKSPACE,
378        description: "`--filter` was passed an empty selector.",
379        exit_code: None,
380    },
381    CodeMeta {
382        name: ERR_AUBE_FILTER_GIT_IO,
383        category: category::MANIFEST_WORKSPACE,
384        description: "A `--filter ...[ref]` selector failed to spawn `git`.",
385        exit_code: None,
386    },
387    CodeMeta {
388        name: ERR_AUBE_FILTER_GIT_FAILED,
389        category: category::MANIFEST_WORKSPACE,
390        description: "The git subprocess for a `--filter ...[ref]` selector exited non-zero.",
391        exit_code: None,
392    },
393    // Engine / CLI
394    CodeMeta {
395        name: ERR_AUBE_UNSUPPORTED_ENGINE,
396        category: category::ENGINE_CLI,
397        description: "One or more packages declared an `engines` constraint incompatible with the running Node/aube and `engine-strict=true`.",
398        exit_code: Some(80),
399    },
400    CodeMeta {
401        name: ERR_AUBE_UNKNOWN_COMMAND,
402        category: category::ENGINE_CLI,
403        description: "The named subcommand isn't a built-in aube command and isn't a script in the manifest.",
404        exit_code: Some(81),
405    },
406    CodeMeta {
407        name: ERR_AUBE_NPM_ONLY_COMMAND,
408        category: category::ENGINE_CLI,
409        description: "The user invoked an npm-only command (`whoami`, `token`, `owner`, `search`, `pkg`, `set-script`) — aube doesn't implement these; use npm.",
410        exit_code: Some(82),
411    },
412    CodeMeta {
413        name: ERR_AUBE_RECURSIVE_NOT_SUPPORTED,
414        category: category::ENGINE_CLI,
415        description: "A command was invoked under `--recursive` but doesn't support recursive execution.",
416        exit_code: None,
417    },
418    CodeMeta {
419        name: ERR_AUBE_COMPLETION_FAILED,
420        category: category::ENGINE_CLI,
421        description: "`aube completion` couldn't invoke `usage` to render the shell completions.",
422        exit_code: None,
423    },
424    CodeMeta {
425        name: ERR_AUBE_REMOVE_PRIOR_INSTALL_DIR,
426        category: category::ENGINE_CLI,
427        description: "Couldn't clean up a prior global install dir before re-installing.",
428        exit_code: None,
429    },
430    CodeMeta {
431        name: ERR_AUBE_CONFIG_NESTED_AUBE_KEY,
432        category: category::ENGINE_CLI,
433        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.",
434        exit_code: None,
435    },
436    CodeMeta {
437        name: ERR_AUBE_CONFLICTING_BUILD_FLAGS,
438        category: category::ENGINE_CLI,
439        description: "`aube add` was passed the same package name in both `--allow-build` and `--deny-build`.",
440        exit_code: None,
441    },
442    // Misc / safety
443    CodeMeta {
444        name: ERR_AUBE_UNSAFE_INDEX_KEY,
445        category: category::MISC_SAFETY,
446        description: "A package index key tried to escape its directory (path traversal defense in depth).",
447        exit_code: Some(90),
448    },
449    CodeMeta {
450        name: ERR_AUBE_UNSAFE_SHEBANG_INTERPRETER,
451        category: category::MISC_SAFETY,
452        description: "A `#!` shebang named an unsafe interpreter when generating a shim — substituted with `node` instead. Surfaced as `tracing::error!` but install continues.",
453        exit_code: Some(91),
454    },
455    CodeMeta {
456        name: ERR_AUBE_PATCHES_TRACKING_WRITE,
457        category: category::MISC_SAFETY,
458        description: "Couldn't write `.aube-applied-patches.json` after applying patches. Non-fatal; next install may miss stale patched entries.",
459        exit_code: None,
460    },
461];