{"record":{"id":"24ab4141bcde23d3","repo":"can1357/oh-my-pi","slug":"binary-not-found-in-archive-extractedbinary","errorCode":null,"errorMessage":"Binary not found in archive: ${extractedBinary}","messagePattern":"Binary not found in archive: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/utils/tools-manager.ts","lineNumber":281,"sourceCode":"\t\t\tawait extractArchive(archivePath, tmp.path());\n\t\t} catch (err) {\n\t\t\tthrow new Error(`Failed to extract ${assetName}: ${err instanceof Error ? err.message : String(err)}`);\n\t\t}\n\n\t\t// Find the binary in extracted files\n\t\t// ast-grep releases the binary directly in the zip, not in a subdirectory\n\t\tlet extractedBinary: string;\n\t\tif (tool === \"sg\") {\n\t\t\textractedBinary = path.join(tmp.path(), config.binaryName + binaryExt);\n\t\t} else {\n\t\t\tconst extractedDir = path.join(tmp.path(), assetName.replace(/\\.(tar\\.gz|zip)$/, \"\"));\n\t\t\textractedBinary = path.join(extractedDir, config.binaryName + binaryExt);\n\t\t}\n\n\t\tif (fs.existsSync(extractedBinary)) {\n\t\t\tawait fs.promises.rename(extractedBinary, binaryPath);\n\t\t} else {\n\t\t\tthrow new Error(`Binary not found in archive: ${extractedBinary}`);\n\t\t}\n\n\t\t// Make executable (Unix only)\n\t\tif (plat !== \"win32\") {\n\t\t\tawait fs.promises.chmod(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Cleanup\n\t\tawait tmp.remove();\n\t\tawait fs.promises.rm(archivePath, { force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n// Install a Python package via uv (preferred) or pip\nasync function installPythonPackage(pkg: string, signal?: AbortSignal): Promise<boolean> {\n\ttry {","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/utils/tools-manager.ts#L263-L299","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// 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`);","typeGuard":"function isBinaryMissingError(err: unknown): err is Error { return err instanceof Error && err.message.startsWith(\"Binary not found in archive: \"); }","tryCatchPattern":"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; }","preventionTips":["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."],"tags":["archive","layout-mismatch","github-releases"],"backgroundTag":"binary-not-found-in-archive","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}