different-ai/openwork · error · ApiError
opencode_unreachable
opencode_unreachable
Error message
OpenCode engine is unavailable
What it means
The OpenWork server proxies a request to the local OpenCode engine (opencode serve) and the underlying fetch failed with a connection-level failure. `isEngineConnectionFailure` classifies the transport error (ECONNREFUSED, socket close, timeout on connect) and `opencodeUnreachableError` converts it into ApiError code `opencode_unreachable`. This means the server could not reach the engine process backing the configured base URL, not that the route is wrong.
Source
Thrown at apps/server/src/server.ts:1490
method,
headers,
body,
}).then(() => {
enginePoolForConfig(input.config)?.reportRequestSuccess(baseUrl);
}).catch((error: unknown) => {
if (workspace) enginePoolForConfig(input.config)?.reportRequestFailure(baseUrl, error, workspace);
// Command failures are surfaced through the OpenCode event stream.
});
return jsonResponse({ ok: true, accepted: true });
}
const forward = async () => {
let response: Response;
try {
response = await loopbackFetch(targetUrl, { method, headers, body });
enginePoolForConfig(input.config)?.reportRequestSuccess(baseUrl);
} catch (error) {
if (workspace) enginePoolForConfig(input.config)?.reportRequestFailure(baseUrl, error, workspace);
if (isEngineConnectionFailure(error)) throw opencodeUnreachableError(error, proxyPath);
throw error;
}
if (response.status === 404 && route?.fallback) {
const fallbackHeaders = headersForEngineConnection(headers, route.fallback);
let fallbackResponse: Response;
try {
fallbackResponse = await loopbackFetch(
buildOpencodeProxyUrl(route.fallback.baseUrl, proxyPath, search),
{ method, headers: fallbackHeaders, body },
);
} catch (error) {
if (workspace) enginePoolForConfig(input.config)?.reportRequestFailure(route.fallback.baseUrl, error, workspace);
if (isEngineConnectionFailure(error)) throw opencodeUnreachableError(error, proxyPath);
throw error;
}
return sanitizeProxyResponse(fallbackResponse);
}View on GitHub (pinned to 2b7df46e8a)
Solutions
- Check the engine process for the config is running (ps / process manager) and restart the server so the engine pool respawns it
- Verify the engine baseUrl/port in the config matches the actual opencode serve endpoint (curl the /health or root path)
- Inspect server logs for the underlying connection error recorded by reportRequestFailure to confirm ECONNREFUSED vs timeout
- If engine startup is slow, add readiness waiting/retry before the first proxy request
Example fix
// before curl http://localhost:4096/session // -> connect ECONNREFUSED // after opencode serve --port 4096 & # then restart apps/server so enginePoolForConfig resolves the live baseUrl curl http://localhost:4096/session
Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch(`${baseUrl}/`).catch(() => null);
if (!res) throw new Error("engine not reachable at " + baseUrl); Type guard
function isOpencodeUnreachable(e: unknown): e is ApiError {
return e instanceof ApiError && e.code === "opencode_unreachable";
} Try / catch
try {
await proxyRequest(req);
} catch (e) {
if (isOpencodeUnreachable(e)) {
showEngineDownBanner(); // surface restart guidance instead of raw failure
} else throw e;
} Prevention
- Run the engine under a process supervisor with automatic restart
- Health-check the engine baseUrl before routing traffic
- Pin engine port in config and verify it on startup
- Monitor enginePool reportRequestFailure signals with alerts
When it happens
Trigger: A proxied request (method + targetUrl built from the engine baseUrl) throws from loopbackFetch with a connection failure; the pool's reportRequestFailure is also recorded for the workspace. Typical when the engine process crashed, was never started for the current config, or is still booting.
Common situations: OpenCode binary missing or crashed after startup; engine port changed or occupied; config points at a stale baseUrl; container/server started before the engine was ready; firewall or proxy blocking loopback.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- Electron desktop helper is unavailable: ${prop}
- latest-mac.yml is missing artifact path/url.
- Failed to fetch latest-mac.yml (${response.status} ${respons
- Request timed out.
- Timed out waiting for server health
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/d865044b213f14d4.
Report an issue: GitHub.