can1357/oh-my-pi · error

blob daemon ${input} responded ${response.status}

Error message

blob daemon ${input} responded ${response.status}

What it means

fetchUnix performs HTTP requests over the blob daemon's Unix socket and throws this error for any non-2xx response from the daemon's control API (/info, /status, /doctor, /probe, /purge, /blob). The message carries the endpoint path and HTTP status so callers can see which daemon operation failed and how.

Source

Thrown at packages/coding-agent/src/blob-broker/daemon.ts:63

const ENSURE_ATTEMPTS = 3;

type DaemonInfo = BlobBrokerInfo & { configKey: string };

/** Session-side callback registry the daemon renders lazy blobs through. */
export interface RenderCallbackHost {
	/** Start (once) and describe the loopback callback server. */
	ensure(): Promise<{ port: number; token: string } | null>;
	/** Register the fetcher answering callbacks for `key`. */
	register(key: string, fetcher: LazyBlobFetcher): void;
}

async function fetchUnix<T>(socket: string, input: string, init?: RequestInit & { timeoutMs?: number }): Promise<T> {
	const response = await fetch(`http://blob-broker.local${input}`, {
		...init,
		unix: socket,
		signal: AbortSignal.timeout(init?.timeoutMs ?? REQUEST_TIMEOUT_MS),
	});
	if (!response.ok) throw new Error(`blob daemon ${input} responded ${response.status}`);
	return (await response.json()) as T;
}

async function probeDaemon(socket: string): Promise<DaemonInfo | null> {
	try {
		return await fetchUnix<DaemonInfo>(socket, "/info", { timeoutMs: PROBE_TIMEOUT_MS });
	} catch {
		return null;
	}
}

async function liveBlobBrokerSocket(projectDir: string): Promise<string | null> {
	if (process.platform === "win32") return null;
	try {
		const client = await daemonClientForProject(projectDir);
		await client.request({ op: "ping" });
		const socket = blobBrokerEndpoint(daemonRuntimeDir(client.projectDir));
		return (await probeDaemon(socket)) ? socket : null;

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the daemon's stderr/log for the underlying cause of the non-2xx response.
  2. Restart the blob daemon (stopQuietly or restart the omp daemon) so it comes back with a fresh exposure.
  3. Upgrade omp so client and daemon speak the same protocol version.
  4. Reduce the request size (e.g. smaller image payload) if the daemon rejected a large upload.

Example fix

// before
const status = await queryBlobBrokerStatus(projectDir); // throws on 500
// after
let status = null;
try {
  status = await queryBlobBrokerStatus(projectDir);
} catch (err) {
  logger.warn("blob daemon query failed; falling back to in-process backend", { err });
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const status = await queryBlobBrokerStatus(projectDir);
} catch (err) {
  logger.debug("blob daemon request failed; using in-process backend", {
    err: err instanceof Error ? err.message : String(err),
  });
  // fall back to LocalBlobBackend or inline base64
}

Prevention

When it happens

Trigger: Any queryBlobBroker* call or DaemonBlobBackend.ensureBlob/ensureLazy when the daemon responds with an error status — daemon internal failure, malformed request body, or the endpoint returning 404/500.

Common situations: Blob daemon crashed mid-request or its exposure died so /probe fails; POSTing to /blob with an oversized payload rejected by the daemon; calling an endpoint the running daemon version does not implement (version skew between client and daemon).

Related errors


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