can1357/oh-my-pi · error · Error
Binary not found in archive: ${extractedBinary}
Error message
Binary not found in archive: ${extractedBinary} What it means
After extraction, downloadTool() computes the expected path of the binary inside the extracted tree (handling the sg-zip flat layout vs nested directories) and renames it into place. If the binary is absent at the computed path, it throws this error including the full expected path, indicating the archive layout differs from what the config expects.
Source
Thrown at packages/coding-agent/src/utils/tools-manager.ts:281
await extractArchive(archivePath, tmp.path());
} catch (err) {
throw new Error(`Failed to extract ${assetName}: ${err instanceof Error ? err.message : String(err)}`);
}
// Find the binary in extracted files
// ast-grep releases the binary directly in the zip, not in a subdirectory
let extractedBinary: string;
if (tool === "sg") {
extractedBinary = path.join(tmp.path(), config.binaryName + binaryExt);
} else {
const extractedDir = path.join(tmp.path(), assetName.replace(/\.(tar\.gz|zip)$/, ""));
extractedBinary = path.join(extractedDir, config.binaryName + binaryExt);
}
if (fs.existsSync(extractedBinary)) {
await fs.promises.rename(extractedBinary, binaryPath);
} else {
throw new Error(`Binary not found in archive: ${extractedBinary}`);
}
// Make executable (Unix only)
if (plat !== "win32") {
await fs.promises.chmod(binaryPath, 0o755);
}
} finally {
// Cleanup
await tmp.remove();
await fs.promises.rm(archivePath, { force: true });
}
return binaryPath;
}
// Install a Python package via uv (preferred) or pip
async function installPythonPackage(pkg: string, signal?: AbortSignal): Promise<boolean> {
try {View on GitHub (pinned to 9690622007)
Solutions
- Open the downloaded archive manually and inspect where the binary actually lives relative to the expected extractedBinary path in the message.
- Pin to a prior release whose layout matched the config.
- Update the tool's TOOLS config (binaryName / extraction-path logic) to match the new archive layout.
- If upstream renamed the executable, adjust binaryName or the sg-flat-layout special case.
- Install the tool manually and reference the system binary.
Defensive patterns
Strategy: fallback
Validate before calling
// pre-check archive layout after download, before relying on auto-install | const names = await listArchiveEntries(archivePath); const hasBinary = names.some(n => n.endsWith(config.binaryName + binaryExt)); if (!hasBinary) throw new Error(`Archive layout mismatch: ${config.binaryName} not present`); Type guard
function isBinaryMissingError(err: unknown): err is Error { return err instanceof Error && err.message.startsWith("Binary not found in archive: "); } Try / catch
try { const p = await toolsManager.download("sg"); } catch (err) { if (err.message.startsWith("Binary not found in archive:")) { /* fall back to pinned version with known layout, or manual install */ } else throw err; } Prevention
- Pin versions whose archive layout matches the config.
- Inspect new upstream releases for directory-structure changes before upgrading.
- Keep binaryName and the flat-layout special cases updated with upstream renames.
- Smoke-test auto-install of each tool in CI to catch layout drift early.
When it happens
Trigger: The archive extracted successfully but config.binaryName (+ platform binaryExt) was not found at the predicted location — upstream changed the internal directory name, nested the binary deeper, renamed the executable, or the binaryName/binaryExt logic mismatched the actual layout.
Common situations: Upstream release restructuring (folder now includes version/os/arch in its name), executables renamed (e.g. with an .exe suffix on a new platform), tools shipping additional wrapper directories, or a wrong binaryName configured for the selected asset.
Related errors
- archive destination exists: ${destSession}
- archive destination exists: ${legacyDestSession}
- archive artifacts destination exists: ${destArtifacts}
- Cannot search archive member(s): ${archiveUnreadable.join(",
- Archive write path must target a file inside the archive
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/24ab4141bcde23d3.
Report an issue: GitHub.