{"record":{"id":"56b261ce207af929","repo":"can1357/oh-my-pi","slug":"invalid-arj-header-signature","errorCode":null,"errorMessage":"Invalid ARJ header signature","messagePattern":"Invalid ARJ header signature","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/arj.ts","lineNumber":64,"sourceCode":"function readCString(bytes: Uint8Array, start: number, end: number, field: string): { value: string; next: number } {\n\tlet terminator = start;\n\twhile (terminator < end && bytes[terminator] !== 0) terminator++;\n\tif (terminator === end) throw new ArchiveError(`Invalid ARJ ${field}: missing terminator`);\n\treturn { value: LEGACY_DECODER.decode(bytes.subarray(start, terminator)), next: terminator + 1 };\n}\n\ninterface ArjBlock {\n\tbodyStart: number;\n\tbodySize: number;\n\tnextOffset: number;\n\tmetadataSize: number;\n\tisEnd: boolean;\n}\n\nfunction parseArjBlock(bytes: Uint8Array, offset: number, options: FormatReadOptions): ArjBlock {\n\tassertRange(bytes, offset, offset + 4, \"header signature\");\n\tif (bytes[offset] !== ARJ_SIGNATURE_0 || bytes[offset + 1] !== ARJ_SIGNATURE_1) {\n\t\tthrow new ArchiveError(\"Invalid ARJ header signature\");\n\t}\n\tconst bodySize = u16(bytes, offset + 2);\n\tif (bodySize === 0)\n\t\treturn { bodyStart: offset + 4, bodySize: 0, nextOffset: offset + 4, metadataSize: 4, isEnd: true };\n\tif (bodySize < 30 || bodySize > ARJ_MAX_BASIC_HEADER) throw new ArchiveError(\"Invalid ARJ basic header size\");\n\tconst bodyStart = offset + 4;\n\tconst bodyEnd = bodyStart + bodySize;\n\tassertRange(bytes, bodyStart, bodyEnd + 4, \"basic header\");\n\tif (crc32(bytes.subarray(bodyStart, bodyEnd)) !== u32(bytes, bodyEnd)) {\n\t\tthrow new ArchiveError(\"Invalid ARJ basic header CRC32\");\n\t}\n\tlet cursor = bodyEnd + 4;\n\tlet extensionCount = 0;\n\tfor (;;) {\n\t\tassertRange(bytes, cursor, cursor + 2, \"extended header size\");\n\t\tconst extensionSize = u16(bytes, cursor);\n\t\tcursor += 2;\n\t\tif (extensionSize === 0) break;","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/arj.ts#L46-L82","documentation":"Every ARJ file begins with the two-byte magic 0x60 0xEA followed by a 16-bit header size. parseArjBlock checks these first two bytes before reading anything else; a mismatch means the data is not an ARJ archive (or the reader's offset is wrong), so it throws immediately rather than misparsing random bytes.","triggerScenarios":"Calling readArj()/parseArjBlock() at an offset whose first two bytes are not 0x60 0xEA: a non-ARJ file passed to the reader, reading past the last local header block with a wrong end-of-archive detection, or an offset/desync bug in custom seeking code.","commonSituations":"Wrong file extension (e.g. a .zip or .exe renamed .arj), a text editor or download manager that altered/trimmed the file, pointing the reader at the wrong offset after hand-editing an index, or passing a Buffer that includes an HTTP header prepended to the archive.","solutions":["Inspect the first bytes: run `xxd -l 16 file.arj` and confirm they are `60 ea`. If not, the file is not ARJ.","Identify the real format (`file file.arj`) and use the matching reader in this library (zip/tar/lzh readers) instead of the ARJ one.","Re-download or re-extract the archive; a corrupted first sector produces exactly this failure.","If you drive parseArjBlock yourself, make sure the offset is a real block start (offsets come from previous parseArjBlock results, never manual arithmetic)."],"exampleFix":"// before: blindly parsing whatever the user supplied\nconst entries = readArj(userUpload);\n// after: verify the magic before parsing\nconst head = new Uint8Array(userUpload.slice(0, 2));\nif (head[0] !== 0x60 || head[1] !== 0xea) {\n  throw new Error(\"Not an ARJ archive: bad magic (expected 60 EA)\");\n}\nconst entries = readArj(userUpload);","handlingStrategy":"validation","validationCode":"function isArjMagic(bytes: Uint8Array): boolean {\n  return bytes.byteLength >= 2 && bytes[0] === 0x60 && bytes[1] === 0xea;\n}\nif (!isArjMagic(data)) throw new Error(\"Not an ARJ archive: bad magic bytes\");","typeGuard":"function isArjBuffer(data: unknown): data is Uint8Array {\n  return data instanceof Uint8Array && data.byteLength >= 2 && data[0] === 0x60 && data[1] === 0xea;\n}","tryCatchPattern":"try {\n  return readArj(data);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message === \"Invalid ARJ header signature\") {\n    throw new Error(\"Input is not an ARJ archive (missing 60 EA signature)\");\n  }\n  throw err;\n}","preventionTips":["Sniff magic bytes (60 EA) or use `file` output to route files to the correct format reader before calling readArj.","Never assume file extensions are accurate; verify content signatures.","Strip any transport wrappers (HTTP headers, concatenated scripts) before parsing."],"tags":["archive","magic-bytes","parsing","wrong-format"],"backgroundTag":"invalid-archive-signature","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}