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

  1. Open the downloaded archive manually and inspect where the binary actually lives relative to the expected extractedBinary path in the message.
  2. Pin to a prior release whose layout matched the config.
  3. Update the tool's TOOLS config (binaryName / extraction-path logic) to match the new archive layout.
  4. If upstream renamed the executable, adjust binaryName or the sg-flat-layout special case.
  5. 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

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


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