different-ai/openwork · error
Could not start OpenWork UI control bridge.
Error message
Could not start OpenWork UI control bridge.
What it means
Thrown by `start` in ui-control-server.mjs after the control HTTP server binds to an ephemeral port on 127.0.0.1, if `server.address()` does not yield a usable port. This is a defensive check: a null port means the listening socket did not report an address, so the UI control bridge cannot be advertised in the discovery file and startup aborts.
Source
Thrown at apps/desktop/electron/ui-control-server.mjs:167
return;
}
if (request.method === "POST" && url.pathname === "/execute") {
sendJsonResponse(response, 200, await runOpenworkControlCommand("execute", await readJsonRequestBody(request)));
return;
}
sendJsonResponse(response, 404, { ok: false, error: "Not found" });
} catch (error) {
console.error("[ui-control] request failed", error);
sendJsonResponse(response, 500, { ok: false, error: "OpenWork UI control request failed." });
}
});
await new Promise((resolve, reject) => {
uiControlServer.once("error", reject);
uiControlServer.listen(0, "127.0.0.1", () => resolve(undefined));
});
const address = uiControlServer.address();
const port = typeof address === "object" && address ? address.port : null;
if (!port) throw new Error("Could not start OpenWork UI control bridge.");
uiControlDiscoveryPath = path.join(app.getPath("userData"), "openwork-ui-control.json");
await writeFile(
uiControlDiscoveryPath,
`${JSON.stringify({ version: 2, app: appName, identifier: appIdentifier, platform: process.platform, baseUrl: `http://127.0.0.1:${port}`, token: uiControlToken }, null, 2)}\n`,
"utf8",
);
// Make the discovery path available to child processes (server → managed OpenCode → plugin).
process.env.OPENWORK_UI_CONTROL_DISCOVERY = uiControlDiscoveryPath;
}
async function stop() {
if (uiControlDiscoveryPath) {
await rm(uiControlDiscoveryPath, { force: true }).catch(() => undefined);
uiControlDiscoveryPath = null;
}
if (!uiControlServer) return;
await new Promise((resolve) => uiControlServer.close(() => resolve(undefined)));
uiControlServer = null;View on GitHub (pinned to 2b7df46e8a)
Solutions
- Investigate why the server closed or address() returned null right after a successful listen
- Add an error/close listener on the server before listen() to capture the real cause
- Retry the start sequence; if persistent, log the address() result for diagnosis
Example fix
// before
uiControlServer.listen(0, '127.0.0.1', () => resolve(undefined));
// after
uiControlServer.on('close', () => console.error('control server closed early'));
uiControlServer.listen(0, '127.0.0.1', () => resolve(undefined)); Defensive patterns
Strategy: retry
Validate before calling
function addressHasPort(address) {
return typeof address === 'object' && address !== null && typeof address.port === 'number' && address.port > 0;
} Try / catch
try {
await startUiControlBridge();
} catch (e) {
if (e.message === 'Could not start OpenWork UI control bridge.') {
await retryWithBackoff(startUiControlBridge, 3);
} else throw e;
} Prevention
- Attach error/close listeners to the server before listen()
- Keep a reference to the server and don't close it during startup
- Log server.address() on failure for diagnosis
- Retry the ephemeral-port bind once on failure
When it happens
Trigger: `uiControlServer.address()` returns null or a non-object (e.g. the server closed between the listen callback and the address read, or an unusual handle/state), making `port` null and tripping the guard.
Common situations: Server closed externally right after startup; IPv6/pipe-shaped address handling assumptions broken; a race where another component tears down the server during init; exotic environments where address() behaves unexpectedly.
Related errors
- OpenWork server did not stay running after startup.
- A valid transferId is required.
- transferId is already active.
- OpenWork server did not report a base URL after startup.
- Desktop integration is disabled for test profiles.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/8f99ef995c45d200.
Report an issue: GitHub.