{
  "$comment": "Machine-readable registry of zipnative's stable error codes (ZipErrorCode in src/types/zip-errors.ts) and diagnostic codes (ZipDiagnosticCode in src/types/zip-types.ts). Codes are frozen from 0.8.0: removal or renaming is semver-major; additions are semver-minor. The error-parity rule of scripts/verify-docs.ts enforces bidirectional sync with the source unions and class membership.",
  "package": "zipnative",
  "verifiedOn": "2026-09-02",
  "errors": [
    { "code": "ZIP_INVALID_OPTION", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "An option value fails validation: compression.level outside 0-9, a non-positive chunkSize, or an argument shape the API forbids.",
      "remedy": "Fix the option value at the call site — the message names the offending option and the accepted range." },
    { "code": "ZIP_INPUT_TOO_LARGE", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "The pure-TS deflate encoder received more than 2 GiB of input in one call.",
      "remedy": "Split the input, or stream it entry by entry." },
    { "code": "ZIP_ENTRY_NOT_FOUND", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "A named entry does not exist where one is required (readEntry, replaceEntry, removeEntry, renameEntry).",
      "remedy": "Names are case-sensitive — iterate entries() to list what the archive holds." },
    { "code": "ZIP_ENTRY_EXISTS", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "A named entry already exists where absence is required (addEntry over an existing name; rename onto an existing name).",
      "remedy": "Use replaceEntry() to overwrite, or removeEntry() the target first — renames never overwrite implicitly." },
    { "code": "ZIP_API_MISUSE", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "A usage contract was violated: toBytes() with addStream() entries, drain-order violations in forward iteration, single-shot data()/skip() reuse, push() after an inflator finished.",
      "remedy": "The message names the contract and the compliant call sequence." },
    { "code": "ZIP_STRICT_DIAGNOSTIC", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "strict: true escalated the first conformance diagnostic of the operation into a thrown error.",
      "remedy": "The message embeds the underlying diagnostic code; handle the archive shape it names, or drop strict and receive diagnostics via onDiagnostic." },
    { "code": "ZIP_INTERNAL", "class": "ZipError", "since": "0.8.0",
      "raisedWhen": "An internal invariant broke — this is a zipnative bug, not an input problem.",
      "remedy": "Report it with a reproduction archive or generator script." },
    { "code": "ZIP_EOCD_NOT_FOUND", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "The input is smaller than 22 bytes, no end-of-central-directory record exists in the trailing scan window, or no candidate is self-consistent (trailing garbage / hostile ambiguity — zipnative refuses to guess).",
      "remedy": "The bytes are not a ZIP archive, are truncated, or carry trailing bytes — verify the source, or remove the trailing bytes if the archive is trusted." },
    { "code": "ZIP_EOCD_INCONSISTENT", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "The end-of-central-directory record contradicts the archive layout: entries-on-disk differs from the total, or the central directory overlaps the record itself.",
      "remedy": "The archive is corrupt or hostile — re-obtain it from the source." },
    { "code": "ZIP_ZIP64_LOCATOR_MISSING", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A zip64 sentinel is set but the zip64 end-of-central-directory locator is absent.",
      "remedy": "The archive is truncated or corrupt — re-obtain it." },
    { "code": "ZIP_ZIP64_EOCD_MISPLACED", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "The zip64 end-of-central-directory record is not where the locator points (and not at the fallback position).",
      "remedy": "Corrupt archive, or an unsupported prepended-data layout — rebuild the archive without the prefix." },
    { "code": "ZIP_CD_INCONSISTENT", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "The central-directory walk contradicts the declared counts or size: it ends early, a record extends past the declared size, or bytes remain beyond the declared entries.",
      "remedy": "The archive is corrupt or hostile — re-obtain it from the source." },
    { "code": "ZIP_RECORD_TRUNCATED", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A fixed or variable-length ZIP record (EOCD, zip64 EOCD, central or local header) or an entry's payload overruns the available bytes.",
      "remedy": "The archive is truncated — verify the transfer completed." },
    { "code": "ZIP_SIGNATURE_MISMATCH", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "An expected PK signature is absent: no local file header at the offset the central directory declares, no EOCD signature at the resolved offset, or a forward stream that does not start with a local header.",
      "remedy": "The archive is corrupt or the bytes are not a ZIP stream." },
    { "code": "ZIP_STREAM_TRUNCATED", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A forward byte stream ended mid-record or mid-entry (iterateZipEntries sources).",
      "remedy": "The stream was cut — verify the producer sent the complete archive." },
    { "code": "ZIP_VALUE_UNREPRESENTABLE", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A 64-bit field exceeds Number.MAX_SAFE_INTEGER (2^53 - 1).",
      "remedy": "Archives this large are not supported — the public API uses number, not bigint." },
    { "code": "ZIP_INVALID_ENTRY_NAME", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A writer-side entry name violates the name rules: empty, NUL bytes, backslashes, absolute paths, or '..' segments (zipnative never writes traversal-capable archives).",
      "remedy": "Use relative, forward-slash paths without traversal segments." },
    { "code": "ZIP_DUPLICATE_ENTRY_NAME", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "Duplicate entry names where uniqueness is required: add() over an existing name, or opening a duplicate-name archive with createZipModifier().",
      "remedy": "Every archive path must be unique; for duplicate-name source archives, extract and rebuild with createZip()." },
    { "code": "ZIP_DEFLATE_TRUNCATED", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A deflate stream ends mid-block: the data ran out before the final block completed.",
      "remedy": "The compressed payload is truncated — re-obtain the archive." },
    { "code": "ZIP_DEFLATE_CORRUPT", "class": "ZipFormatError", "since": "0.8.0",
      "raisedWhen": "A deflate stream is structurally invalid: bad Huffman codes, invalid symbols, out-of-range back-references, LEN/NLEN mismatches, or an unsupported block type.",
      "remedy": "The compressed payload is corrupt — re-obtain the archive." },
    { "code": "ZIP_ENTRY_OVERLAP", "class": "ZipSecurityError", "since": "0.8.0", "cwe": "CWE-405",
      "raisedWhen": "Two entries share bytes: duplicate local-header offsets, an entry extending into another, or an entry claiming to start inside the central directory.",
      "remedy": "Overlapping-entry archives are a decompression-bomb/smuggling shape and are always rejected — there is no opt-out." },
    { "code": "ZIP_CD_LFH_MISMATCH", "class": "ZipSecurityError", "since": "0.8.0", "cwe": "CWE-436",
      "raisedWhen": "A local file header contradicts the central directory on the compression method.",
      "remedy": "Parser-differential archives are rejected — rebuild the archive with a sane tool." },
    { "code": "ZIP_ZIP64_CONTRADICTION", "class": "ZipSecurityError", "since": "0.8.0", "cwe": "CWE-1288",
      "raisedWhen": "A zip64 value contradicts a non-sentinel classic field (zip64 may only REPLACE sentinel fields).",
      "remedy": "Parser-differential archives are rejected — rebuild the archive." },
    { "code": "ZIP_PATH_TRAVERSAL", "class": "ZipSecurityError", "since": "0.8.0", "cwe": "CWE-22/CWE-67",
      "raisedWhen": "An entry name cannot be made safe: it escapes the extraction root (zip-slip — '..' segments, absolute paths, drive letters, UNC prefixes, backslash tricks, NTFS alternate data streams) or is a Windows reserved device name (CON, NUL, COM1..LPT9).",
      "remedy": "The archive is hostile, corrupt, or POSIX-authored with device-name files; pass rejectTraversal: false to silently skip such entries instead (they are never emitted)." },
    { "code": "ZIP_SYMLINK_REJECTED", "class": "ZipSecurityError", "since": "0.8.0", "cwe": "CWE-59",
      "raisedWhen": "An entry is a Unix symlink and rejectSymlinks is on (the default).",
      "remedy": "Pass rejectSymlinks: false to receive the link target as ordinary data (never materialized as a link)." },
    { "code": "ZIP_EXTRACT_DUPLICATE_PATH", "class": "ZipSecurityError", "since": "0.8.0", "cwe": "CWE-694",
      "raisedWhen": "Two entries resolve to the same output path under onDuplicate: 'error' (the default).",
      "remedy": "Pass onDuplicate: 'first' or 'last' to resolve the shadowing deliberately." },
    { "code": "ZIP_CRC_MISMATCH", "class": "ZipDataError", "since": "0.8.0",
      "raisedWhen": "Decompressed bytes fail the declared CRC-32 (central directory, local header, or data descriptor).",
      "remedy": "The data is corrupt; expectedCrc/actualCrc carry both values. Pass verifyCrc: false only if you accept corrupt output." },
    { "code": "ZIP_SIZE_MISMATCH", "class": "ZipDataError", "since": "0.8.0",
      "raisedWhen": "Sizes contradict: the decompressed size differs from the declared size, or local-header sizes/CRC contradict the central directory.",
      "remedy": "The archive metadata lies — treat the archive as corrupt or hostile." },
    { "code": "ZIP_INFLATE_OUTPUT_OVERFLOW", "class": "ZipDataError", "since": "0.8.0",
      "raisedWhen": "Inflate produced more output than the declared or permitted bound.",
      "remedy": "The archive metadata lies about this entry, or raise the relevant limit if the archive is trusted." },
    { "code": "ZIP_DESCRIPTOR_MISMATCH", "class": "ZipDataError", "since": "0.8.0",
      "raisedWhen": "No data-descriptor form (signed/signless × 32/64-bit) matches the measured CRC and sizes of a bit-3 entry.",
      "remedy": "The stream is corrupt or hostile — use openZip() on the complete archive for the authoritative view." },
    { "code": "ZIP_DECOMPRESSION_FAILED", "class": "ZipDataError", "since": "0.8.0",
      "raisedWhen": "The active codec failed mid-decompression (corrupt payload surfaced through node:zlib, DecompressionStream, or an injected codec).",
      "remedy": "The data is corrupt or hostile — the wrapped detail is in the message." },
    { "code": "ZIP_LIMIT_EXCEEDED", "class": "ZipLimitError", "since": "0.8.0", "cwe": "CWE-400/CWE-409",
      "fields": "limit (the ZipLimits key), configured, observed",
      "raisedWhen": "A configured security bound from ZipLimits was exceeded.",
      "remedy": "Raise limits.<limit> explicitly if this archive is trusted — the message names the key and both values." },
    { "code": "ZIP_LIMIT_INVALID", "class": "ZipLimitError", "since": "0.8.0",
      "fields": "limit (the offending key), configured = observed = NaN",
      "raisedWhen": "The limits override itself is invalid: an unknown key, or a non-positive/NaN value.",
      "remedy": "Fix the limits object at the call site — valid keys are listed in the message." },
    { "code": "ZIP_UNSUPPORTED_ENCRYPTION", "class": "ZipUnsupportedError", "since": "0.8.0", "feature": "zipcrypto | strong-encryption",
      "raisedWhen": "An entry is encrypted (ZipCrypto or strong encryption) — encryption is unsupported in 1.x by policy.",
      "remedy": "Check entry.isEncrypted to route around such entries; encrypted entries remain copyable by the modifier without decompression." },
    { "code": "ZIP_UNSUPPORTED_METHOD", "class": "ZipUnsupportedError", "since": "0.8.0", "feature": "method:<n>",
      "raisedWhen": "An entry uses a compression method with no registered codec.",
      "remedy": "registerCodec() one, or re-save the archive with store/deflate." },
    { "code": "ZIP_UNSUPPORTED_MULTI_DISK", "class": "ZipUnsupportedError", "since": "0.8.0", "feature": "multi-disk",
      "raisedWhen": "The archive is multi-disk/spanned (EOCD disk fields, zip64 locator disk count, or an entry starting on another disk).",
      "remedy": "Multi-disk archives are an explicit anti-goal — rebuild as a single archive." },
    { "code": "ZIP_UNSUPPORTED_ZIP64_STREAMING", "class": "ZipUnsupportedError", "since": "0.8.0", "feature": "zip64-streaming",
      "raisedWhen": "An addStream() entry exceeds 4 GiB (Zip64 streaming is not implemented yet).",
      "remedy": "Buffer the content via add() or split it; a per-entry opt-in is tracked on the roadmap." },
    { "code": "ZIP_UNSUPPORTED_CD_LESS_DESCRIPTOR", "class": "ZipUnsupportedError", "since": "0.8.0", "feature": "cd-less-descriptor",
      "raisedWhen": "Forward reading met a data-descriptor (bit 3) entry whose payload cannot be delimited without the central directory: store+bit3, encrypted+bit3, or a custom codec without consumed-byte reporting.",
      "remedy": "Use openZip() on the complete archive instead." },
    { "code": "ZIP_UNSUPPORTED_CODEC_MODE", "class": "ZipUnsupportedError", "since": "0.8.0", "feature": "method:<n>",
      "raisedWhen": "A registered codec supports only the other access mode: stream-only codecs via readEntry(), or codecs without a streaming decompressor via readEntryStream().",
      "remedy": "The message names the compliant call (readEntryStream() or readEntry())." }
  ],
  "diagnostics": [
    { "code": "ZIP_PREPENDED_DATA", "severity": "info",
      "raisedWhen": "Bytes precede the archive (self-extractor stub or concatenation); all offsets were shifted accordingly.",
      "remedy": "Verify the prefix is expected for this file." },
    { "code": "ZIP_MULTIPLE_EOCD", "severity": "info",
      "raisedWhen": "More than one end-of-central-directory signature exists; the last self-consistent candidate was used.",
      "remedy": "Expected for archives whose comment embeds the signature; otherwise inspect the file." },
    { "code": "ZIP_NAME_MISMATCH", "severity": "warning",
      "raisedWhen": "A local header's filename bytes differ from the central directory's; the central directory wins.",
      "remedy": "Rebuild the archive with a sane tool if the divergence is unexpected." },
    { "code": "ZIP_UNICODE_PATH_CONFLICT", "severity": "warning",
      "raisedWhen": "The 0x7075 Unicode Path extra field disagrees with the header name; the header name wins (0x7075 is never acted on).",
      "remedy": "None needed — informational about the producer's inconsistency." },
    { "code": "ZIP_INVALID_UTF8_NAME", "severity": "warning",
      "raisedWhen": "Flag bit 11 claims UTF-8 but the name bytes are invalid UTF-8; decoded as CP437 instead.",
      "remedy": "The producer lied about the encoding — verify names look right." },
    { "code": "ZIP_DUPLICATE_NAME", "severity": "warning",
      "raisedWhen": "The central directory holds duplicate entry names; getEntry() returns the last.",
      "remedy": "Iterate entries() to see every duplicate; extraction under onDuplicate: 'error' throws." },
    { "code": "ZIP_EXTRA_FIELD_MALFORMED", "severity": "warning",
      "raisedWhen": "An extra field overruns its declared length and was skipped.",
      "remedy": "None needed — the malformed field is ignored, the rest is parsed." },
    { "code": "ZIP_ZIP64_EXTRA_IGNORED", "severity": "warning",
      "raisedWhen": "A zip64 extra supplied a value for a non-sentinel classic field; the header value wins.",
      "remedy": "Producer inconsistency — rebuild if unexpected." },
    { "code": "ZIP_TIMESTAMP_NOT_PINNED", "severity": "info",
      "raisedWhen": "defaultDate: 'now' makes the output non-reproducible.",
      "remedy": "Pin a Date (or accept the DOS-epoch default) for deterministic bytes." },
    { "code": "ZIP_NONDETERMINISTIC_CODEC", "severity": "info",
      "raisedWhen": "Timestamps are pinned but a platform codec is in use — bytes are stable per environment, not across zlib builds.",
      "remedy": "Pass deterministic: true to pin the pure-TS encoder for cross-runtime identical bytes." },
    { "code": "ZIP_DEAD_BYTES_RATIO", "severity": "info",
      "raisedWhen": "An incremental save() left more than 50% dead bytes; removed/replaced content remains recoverable in the file.",
      "remedy": "Use saveCompact() for true deletion and a canonical layout." }
  ],
  "sink": {
    "errors": "Thrown; every message starts 'zipnative: ' and names the remedy; err.code is the stable machine key, err.name/instanceof give the class.",
    "diagnostics": "console.warn deduplicated per code per operation by default; onDiagnostic receives all; strict: true throws ZipError code ZIP_STRICT_DIAGNOSTIC."
  }
}
