can1357/oh-my-pi · error · ArchiveError
Tar directory '${formatArchivePathForError(normalized)}' can
Error message
Tar directory '${formatArchivePathForError(normalized)}' cannot contain file data What it means
A member whose raw path ends with '/' or '\' is treated as a directory entry, but tar directory entries carry no payload. The encoder throws when such an entry was given non-empty bytes, since there is no valid way to store file data under a directory-typed header.
Source
Thrown at packages/utils/src/ar/tar.ts:749
directory ? 0x35 : 0x30,
),
);
if (!directory) appendPayload(parts, payload);
}
/** Encode files as a deterministic ustar archive, using PAX records for overflow paths. */
export async function encodeTar(members: Iterable<readonly [string, Uint8Array]>): Promise<Uint8Array> {
const parts: Uint8Array[] = [];
const kinds = new Map<string, "directory" | "file">();
let sequence = 0;
for (const [rawPath, bytes] of members) {
const directory = rawPath.endsWith("/") || rawPath.endsWith("\\");
const normalized = normalizeArchiveEntryPath(rawPath);
if (!normalized) throw new ArchiveError(`Invalid tar member path '${formatArchivePathForError(rawPath)}'`);
const pathBytes = TEXT_ENCODER.encode(normalized);
if (pathBytes.byteLength === 0) throw new ArchiveError("Invalid empty tar member path");
if (directory && bytes.byteLength !== 0) {
throw new ArchiveError(`Tar directory '${formatArchivePathForError(normalized)}' cannot contain file data`);
}
const segments = normalized.split("/");
for (let index = 1; index < segments.length; index++) {
const parent = segments.slice(0, index).join("/");
const kind = kinds.get(parent);
if (kind === "file")
throw new ArchiveError(`Tar member '${formatArchivePathForError(parent)}' is not a directory`);
if (kind === "directory") continue;
kinds.set(parent, "directory");
appendTarEntry(parts, parent, new Uint8Array(0), true, sequence++);
}
const existing = kinds.get(normalized);
if (existing) {
if (directory && existing === "directory") continue;
throw new ArchiveError(`Duplicate tar member path '${formatArchivePathForError(normalized)}'`);
}
kinds.set(normalized, directory ? "directory" : "file");
appendTarEntry(parts, normalized, bytes, directory, sequence++);View on GitHub (pinned to 9690622007)
Solutions
- Remove the trailing slash for entries that carry data
- Move the bytes under a non-directory key like 'dir/index.txt'
- Drop the bytes if the entry is genuinely a directory
Example fix
// before
members.set("docs/", fileBytes);
// after
members.set("docs/readme.txt", fileBytes); Defensive patterns
Strategy: validation
Validate before calling
for (const [key, bytes] of members) {
if ((key.endsWith("/") || key.endsWith("\\")) && bytes.byteLength > 0) {
throw new Error(`'${key}' is a directory but has ${bytes.byteLength} bytes`);
}
} Try / catch
try {
return await createTar(members);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("cannot contain file data")) {
// move data under a non-directory key
}
throw err;
} Prevention
- Reserve trailing-slash keys for empty directory entries only
- Keep directory and file entries in separate conventions in your data model
When it happens
Trigger: createTar called with a key like 'dir/' mapped to non-empty bytes — e.g. a data structure that stores a folder's concatenated contents under the folder's name with a trailing slash.
Common situations: Converting from zip or a virtual FS where directories are implicit and someone appended '/' to a file key; hand-built members maps mixing file/dir conventions.
Related errors
- Invalid old-GNU ${field}
- Invalid tar member path '${formatArchivePathForError(rawPath
- Invalid empty tar member path
- Archive write path must target a file inside the archive
- Archive write path must target a file, not a directory
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/15bf407938559177.
Report an issue: GitHub.