{"record":{"id":"982907a3ac5ff44c","repo":"can1357/oh-my-pi","slug":"multi-volume-arj-archives-are-unsupported","errorCode":null,"errorMessage":"Multi-volume ARJ archives are unsupported","messagePattern":"Multi-volume ARJ archives are unsupported","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/arj.ts","lineNumber":249,"sourceCode":"\tlet bytes: Uint8Array;\n\ttry {\n\t\tbytes = await readAllBytes(source);\n\t} catch (error) {\n\t\tif (error instanceof ArchiveError) throw error;\n\t\tthrow new ArchiveError(`Unable to read ARJ archive: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (bytes.byteLength !== source.size) throw new ArchiveError(\"Invalid ARJ archive: truncated data\");\n\tif (!sniffArj(bytes)) throw new ArchiveError(\"Invalid ARJ archive header\");\n\n\tconst main = parseArjBlock(bytes, 0, options);\n\tif (main.isEnd) throw new ArchiveError(\"Invalid ARJ archive: missing main header\");\n\tconst mainFirstHeaderSize = bytes[main.bodyStart]!;\n\tif (mainFirstHeaderSize < 30 || mainFirstHeaderSize > main.bodySize || bytes[main.bodyStart + 6] !== 2) {\n\t\tthrow new ArchiveError(\"Invalid ARJ main header\");\n\t}\n\tconst mainFlags = bytes[main.bodyStart + 4]!;\n\tif ((mainFlags & 0x01) !== 0) throw new ArchiveError(\"Encrypted ARJ archives are unsupported\");\n\tif ((mainFlags & 0x04) !== 0) throw new ArchiveError(\"Multi-volume ARJ archives are unsupported\");\n\n\tconst entries: ArchiveIndexEntry[] = [];\n\tlet offset = main.nextOffset;\n\tlet metadataSize = main.metadataSize;\n\tlet parsedCount = 0;\n\tfor (;;) {\n\t\tconst block = parseArjBlock(bytes, offset, options);\n\t\tmetadataSize += block.metadataSize;\n\t\tassertIndexSize(metadataSize, options.limits, \"index\");\n\t\tif (block.isEnd) break;\n\t\tassertEntryCount(++parsedCount, options.limits);\n\t\tconst firstHeaderSize = bytes[block.bodyStart]!;\n\t\tif (firstHeaderSize < 30 || firstHeaderSize > block.bodySize) throw new ArchiveError(\"Invalid ARJ local header\");\n\t\tconst hostOs = bytes[block.bodyStart + 3]!;\n\t\tconst flags = bytes[block.bodyStart + 4]!;\n\t\tconst method = bytes[block.bodyStart + 5]!;\n\t\tconst fileType = bytes[block.bodyStart + 6]!;\n\t\tif ((flags & 0x01) !== 0) throw new ArchiveError(\"Encrypted ARJ members are unsupported\");","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/arj.ts#L231-L267","documentation":"Bit 2 (0x04) of the ARJ main header flags byte indicates a multi-volume (split across disks/files) archive. The reader parses a single in-memory byte buffer only and cannot follow continuation volumes, so it rejects such archives immediately after reading the main header.","triggerScenarios":"readArj is called on the first volume of an ARJ multi-volume set (created with ARJ's '-v' volume options); the main header flags byte at bodyStart+4 has 0x04 set.","commonSituations":"Old floppy-disk-era backups split into .arj/.a01/.a02 files; downloading only the first part of a multi-part archive; archives re-split by transfer size limits.","solutions":["Rejoin/extract the full volume set with the original ARJ tool or a compatible extractor (e.g. `arj x archive.arj` with all volumes present), then index the resulting single archive or its extracted files.","Ensure all sibling volumes (.arj, .a01, .a02, ...) are present and, if this library only takes one buffer, pre-merge via an external extractor.","Detect this case in your pipeline and reject multi-volume inputs explicitly before calling readArj.","If you create archives, avoid ARJ volume splitting; produce a single archive within your size limits."],"exampleFix":"// before\nconst entries = await readArj(firstVolumeBytes, options); // throws on multi-volume\n// after\nif (isMultiVolumeArj(firstVolumeBytes)) {\n  const merged = await extractVolumesWithArjTool(volumeDir);\n  const entries = await readArj(merged, options);\n}","handlingStrategy":"validation","validationCode":"function isArjMultiVolume(bytes: Uint8Array): boolean {\n\tconst flags = bytes[mainHeaderBodyStart(bytes) + 4] ?? 0;\n\treturn (flags & 0x04) !== 0;\n}\nif (isArjMultiVolume(bytes)) throw new Error(\"Multi-volume ARJ rejected; rejoin volumes first\");","typeGuard":"function isSingleVolumeArjFlags(flags: number): boolean {\n\treturn (flags & 0x04) === 0;\n}","tryCatchPattern":"try {\n\tentries = await readArj(source, options);\n} catch (e) {\n\tif (e instanceof ArchiveError && e.message === \"Multi-volume ARJ archives are unsupported\") {\n\t\treturn { status: \"multi-volume\", hint: \"extract the full volume set with the ARJ tool first\" };\n\t}\n\tthrow e;\n}","preventionTips":["Check the main header flags (0x04) at ingest time and route multi-volume sets to an external extractor.","Require all sibling volumes (.a01, .a02, ...) to be present before accepting an upload.","Prefer single-volume archive formats for new data."],"tags":["archive","arj","multi-volume","unsupported-feature"],"backgroundTag":"multivolume-archive-unsupported","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}