can1357/oh-my-pi · error · Error

No response body

Error message

No response body

What it means

After a successful (ok) fetch, downloadFile() expects a streaming response body to write to the destination file. If response.body is null — which the fetch spec permits in rare cases even for OK responses — it throws this error rather than silently producing an empty file.

Source

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

		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();
	const architecture = os.arch();

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the download — this is almost always a transient or network-middlebox artifact, not a code bug.
  2. Disable or bypass proxies/VPNs/security appliances that may rewrite the HTTP response.
  3. Verify with curl that the asset URL returns a real body (curl -I to check Content-Length).
  4. If reproducible behind a specific proxy, download the asset manually and place it in the tools directory.
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: confirm the URL serves a body | const head = await fetch(assetUrl, { method: "HEAD" }); if (!head.ok || head.headers.get("content-length") === "0") throw new Error("Asset has no body");

Type guard

function isEmptyBodyError(err: unknown): err is Error { return err instanceof Error && err.message === "No response body"; }

Try / catch

try { const p = await toolsManager.download("sg"); } catch (err) { if (isEmptyBodyError(err)) { await Bun.sleep(2000); /* transient middlebox artifact; retry */ } else throw err; }

Prevention

When it happens

Trigger: The asset fetch returned response.ok === true but response.body === null, so writeResponseBody cannot be called and downloadFile throws "No response body".

Common situations: Unusual proxy or middleware stripping/terminating the body, non-standard HTTP responses intercepted by network appliances, or a corrupted/edge-case response from GitHub's CDN.

Related errors


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