can1357/oh-my-pi · error · Error

Failed to download: ${response.status}

Error message

Failed to download: ${response.status}

What it means

downloadFile() fetches the release asset URL and validates response.ok before streaming the body to disk. Any non-2xx download response (asset removed, bad URL, redirect failure) is rejected with this error, which includes the HTTP status code to identify the cause.

Source

Thrown at packages/coding-agent/src/utils/tools-manager.ts:204

	if (!response.ok) {
		throw new Error(`GitHub API error: ${response.status}`);
	}

	const data = (await response.json()) as { tag_name: string };
	return data.tag_name.replace(/^v/, "");
}

/** Download a tool asset without handing the streaming Response to Bun.write. */
export async function downloadFile(url: string, dest: string, signal?: AbortSignal): Promise<void> {
	const downloadSignal = ptree.combineSignals(signal, TOOL_DOWNLOAD_TIMEOUT_MS);
	let response: Response;
	try {
		response = await fetch(url, {
			signal: downloadSignal,
		});
		if (!response.ok) {
			throw new Error(`Failed to download: ${response.status}`);
		} else if (!response.body) {
			throw new Error("No response body");
		}
		await writeResponseBody(dest, response.body, downloadSignal);
	} catch (err) {
		if (isAbortLikeError(err)) {
			throw new Error(`Download timed out: ${url}`);
		}
		throw err;
	}
}

// Download and install a tool
async function downloadTool(tool: ToolName, signal?: AbortSignal): Promise<string> {
	const config = TOOLS[tool];
	if (!config) throw new Error(`Unknown tool: ${tool}`);

	const plat = os.platform();

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the status: 404 → the asset URL is wrong or the asset was removed for this release; 403 → throttling; 5xx → transient.
  2. Construct the asset URL and open it in a browser to confirm the asset exists for that version.
  3. If a fresh release has missing assets, retry later once the maintainer finishes uploading, or pin to an earlier complete release.
  4. Verify the getAssetName logic / platform mapping matches the upstream naming scheme for the failing platform.
  5. Retry the download; transient 5xx usually resolves.

Example fix

// before: const binaryPath = await toolsManager.download("sg") // throws "Failed to download: 404" | // after: catch, and if /Failed to download: 404/ matches, fall back to system-installed binary; rethrow otherwise
Defensive patterns

Strategy: fallback

Validate before calling

// verify the asset URL exists before invoking the download path | const head = await fetch(assetUrl, { method: "HEAD" }); if (!head.ok) throw new Error(`Asset missing: ${assetUrl} (${head.status})`);

Type guard

function isDownloadHttpError(err: unknown): err is Error { return err instanceof Error && /^Failed to download: \d+$/.test(err.message); }

Try / catch

try { const p = await toolsManager.path("fd"); } catch (err) { if (isDownloadHttpError(err) && err.message.endsWith("404")) { /* fall back to system-installed binary */ } else throw err; }

Prevention

When it happens

Trigger: downloadTool fetches the constructed release asset URL https://github.com/<repo>/releases/download/<tagPrefix><version>/<assetName> and GitHub responds with a non-OK status — typically 404 when the asset name for the current version/platform does not exist, or 403/5xx from GitHub-side issues.

Common situations: Upstream release renamed or dropped the asset for your OS/arch, a brand-new release where assets are still uploading, tagPrefix mismatch producing a malformed URL, or GitHub returning 403 on throttled direct downloads.

Related errors


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