can1357/oh-my-pi · error · ArchiveError

Invalid ar archive: missing alignment byte

Error message

Invalid ar archive: missing alignment byte

What it means

ar members are padded to even offsets. After a member with an odd physical size, the parser must consume one alignment byte; if that byte would land past end-of-input, the archive is malformed and this error is thrown.

Source

Thrown at packages/utils/src/ar/unix-ar.ts:232

		let size = header.physicalSize;
		if (header.bsdNameLength !== undefined) {
			metadataSize += header.bsdNameLength;
			assertIndexSize(metadataSize, options.limits, "index");
			const nameBytes = readMemoryRange(bytes, payloadOffset, payloadOffset + header.bsdNameLength);
			const decoded = decodeBsdName(nameBytes, options.limits);
			name = decoded.name;
			nameByteLength = decoded.byteLength;
			dataOffset += header.bsdNameLength;
			size -= header.bsdNameLength;
		} else if (header.rawName === "//") {
			metadataSize += header.physicalSize;
			assertIndexSize(metadataSize, options.limits, "index");
			longNames = readMemoryRange(bytes, payloadOffset, payloadEnd);
		}
		records.push({ name, nameByteLength, dataOffset, size, mtimeSeconds: header.mtimeSeconds, mode: header.mode });
		assertEntryCount(records.length, options.limits);
		position = payloadEnd + (header.physicalSize & 1);
		if (position > bytes.byteLength) throw new ArchiveError("Invalid ar archive: missing alignment byte");
	}
	return materializeEntries(records, longNames, memoryByteSource(bytes), options);
}

async function readExact(source: ByteSource, start: number, end: number, what: string): Promise<Uint8Array> {
	if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > source.size) {
		throw new ArchiveError(`Invalid ar archive: truncated ${what}`);
	}
	try {
		const bytes = await source.read(start, end);
		if (bytes.byteLength !== end - start) throw new ArchiveError(`Invalid ar archive: truncated ${what}`);
		return bytes;
	} catch (error) {
		if (error instanceof ArchiveError) throw error;
		throw new ArchiveError(error instanceof Error ? error.message : String(error));
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Rebuild the archive with GNU ar so odd-size members are padded.
  2. If you control the writer, always emit a '\n' pad byte after odd-sized payloads.
  3. Compare byte length with a known-good copy to spot single-byte truncation.
  4. Salvage with `ar x` (more tolerant) and repack.

Example fix

// before: writer skips padding
write(header); write(payload);
// after: pad to even offset
write(header); write(payload);
if (payload.length % 2 === 1) writeByte(0x0a);
Defensive patterns

Strategy: validation

Validate before calling

// writer-side guard: pad payloads to even length
if (payload.byteLength % 2 === 1) chunks.push(new Uint8Array([0x0a]));

Try / catch

try {
  return await readUnixAr(source);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('missing alignment byte')) {
    throw new Error('archive not even-byte aligned: likely written by a non-conforming tool');
  }
  throw err;
}

Prevention

When it happens

Trigger: An odd-sized member is the final member and the file ends immediately after its payload, so position = payloadEnd + 1 exceeds bytes.byteLength; or the single pad byte is missing entirely.

Common situations: Archives produced by non-conforming writers that skip the even-alignment pad byte; files truncated by exactly one byte; hand-processed archives where a member was replaced with odd-sized content.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/a32e7c3f9cfdece6. Report an issue: GitHub.