can1357/oh-my-pi · error · ToolError

Archive write path must target a file inside the archive

Error message

Archive write path must target a file inside the archive

What it means

When writing into an archive (zip/sqlite-style targets), the sub-path after the archive prefix must name an entry inside it. An empty sub-path gives the tool nothing to write, so it fails closed.

Source

Thrown at packages/coding-agent/src/tools/write.ts:462

}

interface ResolvedSqliteWritePath {
	absolutePath: string;
	sqlitePath: string;
	table: string;
	key?: string;
	exists: boolean;
}

function isArchivePathNotFound(error: unknown): boolean {
	if (isEnoent(error)) return true;
	return typeof error === "object" && error !== null && "code" in error && error.code === "ENOTDIR";
}

function normalizeArchiveWriteSubPath(rawPath: string): string {
	const normalized = rawPath.replace(/\\/g, "/");
	if (normalized.length === 0) {
		throw new ToolError("Archive write path must target a file inside the archive");
	}
	if (normalized.endsWith("/")) {
		throw new ToolError("Archive write path must target a file, not a directory");
	}

	const parts = normalized.split("/");
	const normalizedParts: string[] = [];
	for (const part of parts) {
		if (!part || part === ".") continue;
		if (part === "..") {
			throw new ToolError("Archive path cannot contain '..'");
		}
		normalizedParts.push(part);
	}

	if (normalizedParts.length === 0) {
		throw new ToolError("Archive write path must target a file inside the archive");
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Append the entry path: write({ path: "archive.zip/dir/file.txt", content })
  2. Remove the trailing slash and supply a concrete file name inside the archive

Example fix

// before
write({ path: "bundle.zip/", content: "x" })
// after
write({ path: "bundle.zip/assets/file.txt", content: "x" })
Defensive patterns

Strategy: validation

Validate before calling

function validateArchiveTarget(p: string): string | null {
  const sub = p.replace(/^[^/]+\//, "").replace(/\\/g, "/");
  if (sub.trim().length === 0 || sub === "") return "empty archive sub-path";
  return null;
}

Try / catch

try {
  await write({ path: target, content });
} catch (e) {
  if (e instanceof ToolError && /must target a file inside the archive/.test(e.message)) {
    throw new Error(`Supply an in-archive entry path, got: ${target}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: write({ path: "archive.zip/" }) or a target whose in-archive sub-path normalizes to empty (e.g. only slashes or dots after the archive root).

Common situations: Trailing-slash typos, forgetting the entry name after the archive path, template substitution leaving the sub-path blank.

Related errors


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