{"record":{"id":"e56a98c69ac5522e","repo":"musistudio/claude-code-router","slug":"codex-app-devtools-port-was-not-available-lasterr","errorCode":null,"errorMessage":"Codex App DevTools port was not available${lastError ? `: ${redactBridgeError(lastError)}` : \".\"}","messagePattern":"Codex App DevTools port was not available(.+?)` : \"\\.\"\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agents/codex/media-preview-bridge.ts","lineNumber":242,"sourceCode":"  }\n}\n\nasync function waitForCodexDevToolsPort(userDataDir: string, timeoutMs: number, stopped: () => boolean): Promise<number> {\n  const file = path.join(userDataDir, codexDevToolsActivePortFile);\n  const deadline = Date.now() + timeoutMs;\n  let lastError: unknown;\n  while (!stopped() && Date.now() < deadline) {\n    try {\n      const firstLine = readFileSync(file, \"utf8\").split(/\\r?\\n/, 1)[0]?.trim();\n      const port = Number(firstLine);\n      if (Number.isInteger(port) && port > 0 && port <= 65535) return port;\n      lastError = new Error(\"DevToolsActivePort did not contain a valid port.\");\n    } catch (error) {\n      lastError = error;\n    }\n    await sleep(codexMediaPreviewPollIntervalMs);\n  }\n  throw new Error(`Codex App DevTools port was not available${lastError ? `: ${redactBridgeError(lastError)}` : \".\"}`);\n}\n\nasync function waitForCodexPageTarget(port: number, timeoutMs: number, stopped: () => boolean): Promise<DevToolsTarget> {\n  const deadline = Date.now() + timeoutMs;\n  let lastError: unknown;\n  while (!stopped() && Date.now() < deadline) {\n    try {\n      const response = await fetch(`http://127.0.0.1:${port}/json/list`, {\n        redirect: \"error\",\n        signal: AbortSignal.timeout(1_000)\n      });\n      if (!response.ok) throw new Error(`CDP target discovery returned HTTP ${response.status}.`);\n      const targets = await response.json() as DevToolsTarget[];\n      const pages = targets.filter((target) => target.type === \"page\" && target.webSocketDebuggerUrl);\n      const target = pages.find(isCodexAppPageTarget) || pages.find((entry) => (entry.url || \"\").startsWith(\"app://\"));\n      if (target) return target;\n    } catch (error) {\n      lastError = error;","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/agents/codex/media-preview-bridge.ts#L224-L260","documentation":"waitForCodexDevToolsPort polls the Codex App's DevToolsActivePort file/socket until a timeout and throws this when no valid CDP port was obtained. The message includes the redacted last underlying error (redactBridgeError) explaining why each poll failed, or '.' when there was no specific error.","triggerScenarios":"Starting the media preview bridge while the Codex desktop app is not running, was launched without remote debugging enabled, crashed during startup, or when the DevToolsActivePort file is missing/contains a non-numeric port, and the poll deadline expires.","commonSituations":"Codex app not installed or not launched; app launched without the --remote-debugging-port/DevTools flags; slow machine where startup exceeds the configured timeout; sandboxed environment where the DevTools port file isn't readable.","solutions":["Verify the Codex App is actually running and was launched with DevTools/CDP enabled","Increase the timeout argument passed to waitForCodexDevToolsPort for slow startups","Read the redacted lastError in the message to identify the underlying cause (file missing vs invalid port) and fix that (permissions, correct user-data-dir)","Retry the bridge start after the app has fully booted"],"exampleFix":"// before\nconst port = await waitForCodexDevToolsPort(3000, () => false);\n// after\nconst port = await waitForCodexDevToolsPort(30000, () => false); // longer timeout, app started first","handlingStrategy":"retry","validationCode":"// before bridging, confirm the DevToolsActivePort file exists and parses\nimport { existsSync, readFileSync } from 'node:fs';\nfunction devToolsPortReady(path: string): boolean {\n  try { const [port] = readFileSync(path, 'utf8').trim().split('\\n'); return /^\\d+$/.test(port); }\n  catch { return false; }\n}","typeGuard":"null","tryCatchPattern":"try { const port = await waitForCodexDevToolsPort(timeoutMs, stopped); } catch (e) { if (e instanceof Error && e.message.includes('DevTools port was not available')) { /* relaunch app with debugging enabled, retry with backoff */ } else throw e; }","preventionTips":["Launch the Codex App with DevTools enabled before starting the bridge","Use generous timeouts on slow machines","Treat the redacted lastError text as the diagnostic source"],"tags":["devtools","cdp","codex-app","timeout"],"backgroundTag":"devtools-port-unavailable","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}