can1357/oh-my-pi · error · ArchiveError
Archive member '${formatArchivePathForError(memberPath)}' is
Error message
Archive member '${formatArchivePathForError(memberPath)}' is a sparse file and cannot be read What it means
TarMemberSource.read() refuses to return data for tar members stored with GNU sparse encoding. Sparse files are represented by a map of holes/data extents that this reader does not materialize, so attempting to read the member's content is rejected instead of returning silently wrong bytes. The member path is included (sanitized via formatArchivePathForError) to identify the offending entry.
Source
Thrown at packages/utils/src/ar/tar.ts:69
interface PendingTarLink {
kind: "hard link" | "symlink";
targetPath: string;
}
class TarMemberSource implements MemberSource {
readonly #buffer: Uint8Array;
readonly #dataOffset: number;
readonly #sparse: boolean;
constructor(buffer: Uint8Array, dataOffset: number, sparse: boolean) {
this.#buffer = buffer;
this.#dataOffset = dataOffset;
this.#sparse = sparse;
}
async read(size: number, memberPath: string): Promise<Uint8Array> {
if (this.#sparse) {
throw new ArchiveError(
`Archive member '${formatArchivePathForError(memberPath)}' is a sparse file and cannot be read`,
);
}
if (size > this.#buffer.byteLength - this.#dataOffset) {
throw new ArchiveError(`Archive member '${formatArchivePathForError(memberPath)}' is truncated`);
}
const bytes = this.#buffer.subarray(this.#dataOffset, this.#dataOffset + size);
if (bytes.byteLength !== size) {
throw new ArchiveError(`Archive member '${formatArchivePathForError(memberPath)}' has an invalid size`);
}
return bytes;
}
}
function readTarString(buffer: Uint8Array, offset: number, length: number): string {
const limit = Math.min(offset + length, buffer.byteLength);
let end = offset;
while (end < limit && buffer[end] !== 0) end++;View on GitHub (pinned to 9690622007)
Solutions
- Re-create the archive without sparse encoding: tar --no-sparse (or omit -S) so files are stored fully materialized
- Skip sparse members in your extraction loop — check the entry type before calling read() and handle them separately
- Use a tar reader with sparse support if you need the original hole structure
- On the source system, zero-out-and-copy the file (cp --sparse=never) before archiving
Example fix
// before: blindly reading every member
for (const entry of index) {
const data = await entry.source.read(entry.size, entry.path);
}
// after: skip sparse members
for (const entry of index) {
if (entry.sparse) { console.warn(`skipping sparse member ${entry.path}`); continue; }
const data = await entry.source.read(entry.size, entry.path);
} Defensive patterns
Strategy: validation
Validate before calling
// inspect entries before reading; skip anything flagged sparse
for (const entry of index) {
if (entryIsSparse(entry)) {
console.warn(`skipping sparse member: ${entry.path}`);
continue;
}
} Type guard
function isSparseMember(entry: { sparse?: boolean }): entry is { sparse: true } {
return entry.sparse === true;
} Try / catch
try {
const data = await entry.source.read(entry.size, entry.path);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("sparse file")) {
throw new Error(`${entry.path} is GNU-sparse; recreate the tar without --sparse or use a sparse-aware reader`);
}
throw err;
} Prevention
- Create archives with tar --no-sparse (never pass -S/--sparse) when consumers use this reader
- Enumerate entries and check for sparse flags before attempting extraction
- Document to archive producers that GNU sparse members are not readable here
- Handle the error per-member so one sparse file does not abort the whole extraction run
When it happens
Trigger: Extracting or reading content of a tar entry whose header flags mark it as GNU sparse (typeflag 'S', or GNU sparse extensions in the ustar header — GNU.sparse.* PAX keys / sparse isextended blocks). The reader parses the member but any call to MemberSource.read(size, path) on it throws.
Common situations: Archives created by GNU tar with --sparse (or -S) on files with large zero regions (VM disks, database files, log files); old backup archives of filesystem images; encountering sparse members while listing works fine but extraction fails.
Related errors
- Invalid tar octal value: ${value}
- Truncated embedded addon archive entry: ${filename}
- Unsafe embedded addon archive entry: ${filename}
- Unsupported embedded addon archive entry type ${typeflag}: $
- ARJ member '${memberPath}' uses unsupported compression meth
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5fd6187598b7e5c2.
Report an issue: GitHub.