can1357/oh-my-pi · error · ToolError

Cannot search archive member(s): ${archiveUnreadable.join(",

Error message

Cannot search archive member(s): ${archiveUnreadable.join(", ")}. Read the member with `read <archive>:<member>` and inspect the returned text, or pass a UTF-8 text member.

What it means

Every path passed to grep was an archive member selector (e.g. archive.tar.gz:inner.txt) that could not be materialized as searchable text — typically because the member is binary or not decodable as UTF-8 — and there were no other searchable or virtual paths to fall back on. The tool surfaces this reason instead of letting the scope resolver emit a confusing "path not found".

Source

Thrown at packages/coding-agent/src/tools/grep.ts:1014

					cwd: this.session.cwd,
					settings: this.session.settings,
					signal,
					archiveDisplayMap,
					localProtocolOptions: this.session.localProtocolOptions,
					skills: this.session.skills,
					sessionFile: this.session.getSessionFile() ?? undefined,
				});
				const searchablePaths = internalResolution.paths;
				const { virtualResources, virtualPathSet, virtualInputIndexes } = internalResolution;
				const rangesByAbsPath = new Map<string, LineRange[]>();

				if (
					archiveUnreadable.length > 0 &&
					searchablePaths.length === archiveUnreadable.length &&
					virtualResources.length === 0
				) {
					// All inputs were archive selectors we couldn't materialize; surface the
					// reason instead of a downstream "path not found" from the scope resolver.
					throw new ToolError(
						`Cannot search archive member(s): ${archiveUnreadable.join(", ")}. ` +
							`Read the member with \`read <archive>:<member>\` and inspect the returned text, ` +
							`or pass a UTF-8 text member.`,
					);
				}
				const normalizedContextBefore = this.#contextOverride ?? this.session.settings.get("grep.contextBefore");
				const normalizedContextAfter = this.#contextOverride ?? this.session.settings.get("grep.contextAfter");
				const ignoreCase = !(caseSensitive ?? true);
				const useGitignore = gitignore ?? true;
				const patternHasNewline = normalizedPattern.includes("\n") || normalizedPattern.includes("\\n");
				const effectiveMultiline = patternHasNewline;

				let searchPath: string;
				let scopePath: string;
				let globFilter: string | undefined;
				let isDirectory: boolean;
				let multiTargets: ResolvedSearchTarget[] | undefined;

View on GitHub (pinned to 9690622007)

Solutions

  1. Extract the member's text via `read <archive>:<member>` and inspect the returned text
  2. Pass a UTF-8 text member of the archive instead of the binary one
  3. Include at least one normal (non-archive) path in the semicolon-delimited `path` list so the search can proceed on the other targets

Example fix

// before
await grep({ pattern: "version", path: "app.tar.gz:assets/logo.png" });
// after
const pkg = await read("app.tar.gz:package.json");
await grep({ pattern: "version", path: "app.tar.gz:package.json" });
Defensive patterns

Strategy: fallback

Try / catch

try {
  await grep({ pattern, path: "data.tar.gz:blob.bin" });
} catch (err) {
  if (err instanceof ToolError && err.message.startsWith("Cannot search archive member")) {
    const text = await read("data.tar.gz:README.md"); // fall back to a text member
  } else throw err;
}

Prevention

When it happens

Trigger: grep with path set exclusively to archive member selectors whose contents cannot be extracted as UTF-8 text, e.g. `grep tgz:data.tar.gz:bin/image.png` or a compressed binary member.

Common situations: Grepping inside a tar/zip for a binary asset by mistake; typos in the member name selecting a binary file; expecting the tool to search binary contents.

Related errors


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