Diagnostics (DIAG_* reference)

Every DIAG_* code AfterPack emits, what triggered it, how to resolve it, and the exit codes a build returns.

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.

SeverityMeaningWhat happens
InfoExpected behavior: you already acknowledged this, or the engine took a safe, correctness-preserving fallback.Build succeeds, output written.
ErrorYour 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.
CriticalAn 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.

CodeMeaning
0Success. Every collected file was obfuscated and written. --help and --version also exit 0.
1Failure. No usable output was produced, and a message on stderr names what happened.
2Partial. Some files shipped unobfuscated — only reachable with allowUnobfuscated.
3Size cap. inflation.max could not reach the complexity target (DIAG_SIZE_CAP_REACHED).
4Reserved. A Pro feature requested without a key, or a lapsed entitlement. Documented, never emitted.
5Reserved. Runtime reflection detected without reflection.allow. Documented, never emitted.
64Misuse. 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 in package.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/.cjs files. 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.exclude to carve out a file that must ship untouched.
  • A failed verify or a failed audit scan.
  • Any code below carrying Error or Critical severity.

Fail closed, no exceptions

A Pro build either ships Pro-level protection or it doesn't ship.

AfterPack fail-closed behavior on a Pro buildPro build calls the cloudrequests a cloud-side transformCloud reachableCloud unreachableretrying…Obfuscated output written↻ Retries exhaustedexponential backoff, still down✓ EXIT 0build succeeds✕ EXIT 1loud error, build failsSuccess pathcloud-obfuscated output onlyFail-closed guarantee· no Free-tier fallback· no --allow-degraded· never writes plaintextAfterPack never writes an unprotected file in place of a requested Pro one.

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

CodeMeaning
DIAG_TARGET_REACHEDInfo. The engine reached the requested complexity.
DIAG_TARGET_NOT_REACHEDInfo. The depth cap stopped inflation before the target; output is valid, just shallower.
DIAG_INFLATION_BUDGET_HITInfo. A size-adding stage stopped at the resolved inflation budget before reaching the target. Output is valid and floor-preserving.
DIAG_SIZE_CAP_REACHEDAn explicit inflation.max blocked reaching the target. Raise it, lower complexity, or exclude the file with paths.exclude.
DIAG_PATH_EXCLUDEDInfo. A file matched paths.exclude and shipped byte for byte; its bytes are counted as user-excluded.
DIAG_SURVIVING_READABLE_LITERALMore readable string literals remain than strings.leaks.max allows. Raise complexity, lower strings.minLength, or raise the limit.
DIAG_SOURCE_MAP_INTEGRITYError. A supplied upstream .map failed validation against its input. No output is written.
DIAG_OUTPUT_UNPARSEABLEError, and a signal of an engine bug. Re-parsing the output failed, so no output is written.
DIAG_PARSE_ERRORError. 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_ERRORError. The engine config JSON didn't deserialize.
DIAG_INPUT_TOO_LARGEA single input exceeded the size ceiling.
DIAG_BATCH_TOO_LARGEA batch past 500 MB total (413, non-retryable); the build fails closed.
DIAG_ENGINE_BUG_NO_PROGRESSCritical. The engine made no progress. This is an engine bug.
DIAG_ENGINE_BUG_NO_APPLICABLE_TRANSFORMCritical. No applicable transform was available. This is an engine bug.
DIAG_ENGINE_BUG_PROCESS_THREAD_PANICCritical. A worker thread panicked. This is an engine bug.

Build plugin / CLI

CodeMeaning
DIAG_DIRECTIVE_UNKNOWNAn @afterpack directive named an unrecognised key. Check it against which keys a region can override.
DIAG_DIRECTIVE_UNSUPPORTED_FORMThe marker form can't be placed (// @afterpack … line comment, or @afterpack:begin/:end span). Use /* @afterpack … */ block markers.
DIAG_DIRECTIVE_NOT_IMPLEMENTEDA 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_UNRESOLVEDInfo. 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_ENDA /* @afterpack end */ with no open block above it.
DIAG_DIRECTIVE_REGION_TO_FILEidentifiers.globals.rename can't be scoped to a region, so a directive naming it is ignored and reported.
DIAG_DIRECTIVE_COVERAGE_UNVERIFIEDA bundled chunk carrying directives had no decodable source map, so the marked regions couldn't be located in its bytes.
DIAG_DIRECTIVE_TARGET_ELIMINATEDDirectives 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_OBFUSCATEDThe 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

CodeMeaning
DIAG_INVALID_KEYMissing, malformed, or unknown key (401). Check AFTERPACK_key, or issue a fresh key from the dashboard.
DIAG_KEY_REVOKEDThe API key was revoked (401).
DIAG_KEY_EXPIREDThe API key expired (401).
DIAG_KEY_OUT_OF_SCOPEThe request IP is outside the key's allowed range (403).
DIAG_MISSING_IPThe request carried no client IP (400).
DIAG_INVALID_MANIFESTMalformed multipart body, or a missing batch manifest part.
DIAG_FILE_TOO_LARGEA single file exceeded the per-file byte ceiling (413). Split it, or exclude it with paths.exclude.
DIAG_ENTITLEMENT_LAPSEDSubscription payment past due (402); update billing to keep building.
DIAG_QUOTA_EXHAUSTEDThe 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_LIMITEDRate limit exceeded (429); honour Retry-After.
DIAG_RATE_CHECK_UNAVAILABLEThe rate-limit check itself failed, so the request fails closed (503); retry.
DIAG_BUILD_DEADLINEThe wall-clock budget elapsed before every file started; the unfinished files are named.
DIAG_ENGINE_FAILUREThe 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.

CodeMeaning
DIAG_REFLECTION_nameIntrospection_ACKNOWLEDGEDInfo. 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.
nameIntrospectionError. 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.
Stray artifacts are caught by the audit

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 @afterpack grammar 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.