{"record":{"id":"d348b68329f9c2b3","repo":"can1357/oh-my-pi","slug":"failed-to-extract-assetname-err-instanceof-e","errorCode":null,"errorMessage":"Failed to extract ${assetName}: ${err instanceof Error ? err.message : String(err)}","messagePattern":"Failed to extract (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/utils/tools-manager.ts","lineNumber":265,"sourceCode":"\t\treturn binaryPath;\n\t}\n\n\t// Download archive\n\tconst archivePath = path.join(TOOLS_DIR, assetName);\n\tawait downloadFile(downloadUrl, archivePath, signal);\n\n\t// Extract\n\tconst tmp = await TempDir.create(\"@omp-tools-extract-\");\n\n\ttry {\n\t\tif (!assetName.endsWith(\".tar.gz\") && !assetName.endsWith(\".zip\")) {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\ttry {\n\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","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/utils/tools-manager.ts#L247-L283","documentation":"extractArchive() failing (corrupt archive, unsupported compression inside, disk issues) is wrapped by downloadTool() into this error, embedding the asset name and the underlying message. The error chain is preserved so the root cause is visible.","triggerScenarios":"The downloaded .tar.gz or .zip asset fails during extractArchive(archivePath, tmp.path()) — truncated/corrupt download, HTML error page saved as an archive (e.g. a 404 body that slipped through), or an archive using an algorithm the extractor rejects.","commonSituations":"Interrupted downloads from unstable networks, upstream re-releasing/overwriting a release asset mid-download (checksum mismatch), CDN serving an error page, full disk during extraction, or genuine archive corruption.","solutions":["Read the embedded inner message to identify the root cause (e.g. \"invalid zip\", \"unexpected end of file\").","Delete any cached/partial downloads and retry — a truncated asset is the most common cause.","Verify the asset's integrity (SHA published by upstream) and confirm the file isn't an HTML error page (file archive.tar.gz).","Pin to a stable prior release if upstream just re-published a broken asset.","Check available disk space in the temp/tools directories."],"exampleFix":"// before: await toolsManager.download(\"fd\") // throws \"Failed to extract fd-...zip: unexpected EOF\" | // after: catch, and if err.message.startsWith(\"Failed to extract\"), clear cache and retry once; rethrow otherwise","handlingStrategy":"retry","validationCode":"// verify asset integrity before extraction by checking size | const head = await fetch(assetUrl, { method: \"HEAD\" }); const expectedSize = Number(head.headers.get(\"content-length\") ?? 0); const actualSize = (await fs.stat(localPath)).size; if (actualSize !== expectedSize) throw new Error(\"Truncated download; refetch\");","typeGuard":"function isExtractionFailure(err: unknown): err is Error { return err instanceof Error && /^Failed to extract .+:/.test(err.message); }","tryCatchPattern":"try { const p = await toolsManager.download(\"sg\"); } catch (err) { if (isExtractionFailure(err)) { /* delete partial download, retry once, then surface inner cause */ } else throw err; }","preventionTips":["Verify downloaded sizes/checksums against upstream-published SHAs.","Ensure adequate free disk space in temp directories.","Retry once on extraction failure — truncated downloads are the top cause.","Don't download while a release is actively being re-published."],"tags":["archive","extraction","corrupt-download"],"backgroundTag":"archive-extraction-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}