AfterPack never ships weaker protection than you asked for. When something can't be resolved automatically, the build stops with a named DIAG_* code and a specific fix.
Severity levels
Every diagnostic AfterPack emits is exactly one of three severities. There is no Warning severity.
| Severity | Meaning | What happens |
|---|---|---|
| Info | Expected behavior: you already acknowledged this, or the engine took a safe, correctness-preserving fallback. | Build succeeds, output written. |
| Error | Your stated requirements can't be met without a change on your end (a size cap that's too tight, a parse error, an invalid key). | Non-zero exit, no output written. |
| Critical | An engine bug. No action on your end fixes this. | Non-zero exit, no output written, always. File a report. |
Exit codes
npx afterpack@latest returns one of seven codes. Script against them directly.
| Code | Meaning |
|---|---|
0 | Success. Every collected file was obfuscated and written. --help and --version also exit 0. |
1 | Failure. No usable output was produced, and a message on stderr names what happened. |
2 | Partial. Some files shipped unobfuscated — only reachable with allowUnobfuscated. |
3 | Size cap. inflation.max could not reach the complexity target (DIAG_SIZE_CAP_REACHED). |
4 | Reserved. A Pro feature requested without a key, or a lapsed entitlement. Documented, never emitted. |
5 | Reserved. Runtime reflection detected without reflection.allow. Documented, never emitted. |
64 | Misuse. An unknown flag or command, a malformed value, or a duplicated path argument. |
What makes a build exit 64
Usage problems stop the run before it starts.
- An unknown flag, an unknown command, a malformed value, or more than one positional path. The usage line reprints with the error.
What makes a build exit 1
- No
<path>argument and no build output found (dist/,build/,out/,.output/,.next/, or the conventional directory of the bundler named inpackage.json). AfterPack never reads a bundler's own config file, so a custom output directory is not detected this way. - The build directory does not exist, or contains no
.js/.mjs/.cjsfiles. An empty build directory fails the build. The usual cause is pointing at the wrong directory or running before the build. - A file failed outright, or came back with empty output where the input was non-empty.
- A file the engine cannot obfuscate safely. The build fails and names the affected files. Use
paths.excludeto carve out a file that must ship untouched. - A failed
verifyor a failedauditscan. - Any code below carrying
ErrororCriticalseverity.
Fail closed, no exceptions
A Pro build either ships Pro-level protection or it doesn't ship.
Once a key resolves, from AFTERPACK_key or afterpack.json, the build is a cloud build. If the cloud call fails for any reason, the engine returns an error and the build exits 1. It does not fall back to the local Free engine, and it does not emit unobfuscated output. There is no flag that ships unobfuscated output. Stopping the build puts the problem where you can see it.
This contract does not change with the invocation path: npx afterpack@latest and a plugin's auto-run on bundle close behave identically.
Exit codes in CI
npx afterpack@latest dist/ || {
echo "AfterPack failed: nothing shipped. See the error above."
exit 1
}Non-zero stops the pipeline. The reason appears on stderr and in the codes below.
Complete reference
Engine
| Code | Meaning |
|---|---|
DIAG_TARGET_REACHED | Info. The engine reached the requested complexity. |
DIAG_TARGET_NOT_REACHED | Info. The depth cap stopped inflation before the target; output is valid, just shallower. |
DIAG_INFLATION_BUDGET_HIT | Info. A size-adding stage stopped at the resolved inflation budget before reaching the target. Output is valid and floor-preserving. |
DIAG_SIZE_CAP_REACHED | An explicit inflation.max blocked reaching the target. Raise it, lower complexity, or exclude the file with paths.exclude. |
DIAG_PATH_EXCLUDED | Info. A file matched paths.exclude and shipped byte for byte; its bytes are counted as user-excluded. |
DIAG_SURVIVING_READABLE_LITERAL | More readable string literals remain than strings.leaks.max allows. Raise complexity, lower strings.minLength, or raise the limit. |
DIAG_SOURCE_MAP_INTEGRITY | Error. A supplied upstream .map failed validation against its input. No output is written. |
DIAG_OUTPUT_UNPARSEABLE | Error, and a signal of an engine bug. Re-parsing the output failed, so no output is written. |
DIAG_PARSE_ERROR | Error. An input chunk didn't parse. AfterPack runs strictly post-build, on emitted JavaScript: compile first, then point it at the JS output. |
DIAG_CONFIG_PARSE_ERROR | Error. The engine config JSON didn't deserialize. |
DIAG_INPUT_TOO_LARGE | A single input exceeded the size ceiling. |
DIAG_BATCH_TOO_LARGE | A batch past 500 MB total (413, non-retryable); the build fails closed. |
DIAG_ENGINE_BUG_NO_PROGRESS | Critical. The engine made no progress. This is an engine bug. |
DIAG_ENGINE_BUG_NO_APPLICABLE_TRANSFORM | Critical. No applicable transform was available. This is an engine bug. |
DIAG_ENGINE_BUG_PROCESS_THREAD_PANIC | Critical. A worker thread panicked. This is an engine bug. |
Build plugin / CLI
| Code | Meaning |
|---|---|
DIAG_DIRECTIVE_UNKNOWN | An @afterpack directive named an unrecognised key. Check it against which keys a region can override. |
DIAG_DIRECTIVE_UNSUPPORTED_FORM | The marker form can't be placed (// @afterpack … line comment, or @afterpack:begin/:end span). Use /* @afterpack … */ block markers. |
DIAG_DIRECTIVE_NOT_IMPLEMENTED | A program-only key written in a directive (for example reflection.allow), or a redundant directive like skip=false. See Which keys for what a region can actually override. |
DIAG_DIRECTIVE_SOURCE_UNRESOLVED | Info. A directive sits in a module whose position could not be mapped into the build output, so it was not applied. Move the marked code into an imported module. |
DIAG_DIRECTIVE_DANGLING_END | A /* @afterpack end */ with no open block above it. |
DIAG_DIRECTIVE_REGION_TO_FILE | identifiers.globals.rename can't be scoped to a region, so a directive naming it is ignored and reported. |
DIAG_DIRECTIVE_COVERAGE_UNVERIFIED | A bundled chunk carrying directives had no decodable source map, so the marked regions couldn't be located in its bytes. |
DIAG_DIRECTIVE_TARGET_ELIMINATED | Directives were located in a chunk's map but matched zero ranges. The marked code was tree-shaken, or the coordinates didn't line up. |
DIAG_ALREADY_OBFUSCATED | The collected files already carry AfterPack's own obfuscated output from a prior run, matched against the protection receipt by content hash. AfterPack output is not idempotent — rebuild from source first. |
Cloud API
| Code | Meaning |
|---|---|
DIAG_INVALID_KEY | Missing, malformed, or unknown key (401). Check AFTERPACK_key, or issue a fresh key from the dashboard. |
DIAG_KEY_REVOKED | The API key was revoked (401). |
DIAG_KEY_EXPIRED | The API key expired (401). |
DIAG_KEY_OUT_OF_SCOPE | The request IP is outside the key's allowed range (403). |
DIAG_MISSING_IP | The request carried no client IP (400). |
DIAG_INVALID_MANIFEST | Malformed multipart body, or a missing batch manifest part. |
DIAG_FILE_TOO_LARGE | A single file exceeded the per-file byte ceiling (413). Split it, or exclude it with paths.exclude. |
DIAG_ENTITLEMENT_LAPSED | Subscription payment past due (402); update billing to keep building. |
DIAG_QUOTA_EXHAUSTED | The build's byte total exceeds the remaining allowance, or the overage spend cap is reached. Checked once per build, before any file dispatches, so nothing already-protected degrades. Top up, upgrade, or raise the cap; the error carries the link. |
DIAG_RATE_LIMITED | Rate limit exceeded (429); honour Retry-After. |
DIAG_RATE_CHECK_UNAVAILABLE | The rate-limit check itself failed, so the request fails closed (503); retry. |
DIAG_BUILD_DEADLINE | The wall-clock budget elapsed before every file started; the unfinished files are named. |
DIAG_ENGINE_FAILURE | The cloud engine failed on a file. |
Runtime reflection
AfterPack detects these runtime-reflection patterns: functionToString, nameIntrospection, argumentsCallee, prototypeChain, decoratorMetadata and constructorIntrospection. Each detection carries a code; which one depends on whether you acknowledged the pattern.
| Code | Meaning |
|---|---|
DIAG_REFLECTION_nameIntrospection_ACKNOWLEDGED | Info. The pattern was detected and preserved because you acknowledged it. One code per pattern; the name in the middle is any of the patterns above. |
nameIntrospection | Error. The pattern was detected and not acknowledged, so the build stops rather than shipping code whose runtime read would break. Acknowledge it with reflection.allow, which also accepts angular and nestjs, or take the file out with paths.exclude. |
Shipping a stray .map or an un-obfuscated sibling into your deploy tree is caught by npx afterpack@latest audit, not by the build.
If a code is missing or wrong, open an issue: github.com/afterpack-dev/afterpack/issues.
Run with --diagnostics.level=all for the full diagnostic stream on any build, rather than the default summary. In a plugin build the same switch is the diagnostics.level option or AFTERPACK_diagnostics_level.
Next
- CLI reference: every flag the codes above reference.
- Configuration: the config keys these codes reference.
- Directives: the full
@afterpackgrammar behind the directive codes above. - Best practices: the patterns that avoid these diagnostics in the first place.
- Tiers: what's Free vs. what needs a
key.