{"record":{"id":"efb2a1b6c4cd8570","repo":"t8y2/dbx","slug":"dbx-mcp-process-is-not-available","errorCode":null,"errorMessage":"DBX MCP process is not available","messagePattern":"DBX MCP process is not available","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/dbx-core/assets/pi-mcp-bridge.mjs","lineNumber":111,"sourceCode":"      message = JSON.parse(line);\n    } catch {\n      return;\n    }\n    if (message.id == null) return;\n    const request = pending.get(String(message.id));\n    if (!request) return;\n    pending.delete(String(message.id));\n    clearTimeout(request.timer);\n    if (message.error) {\n      request.reject(new Error(message.error.message ?? JSON.stringify(message.error)));\n    } else {\n      request.resolve(message.result);\n    }\n  });\n\n  const send = (message) => {\n    if (closed || !child.stdin.writable) {\n      throw new Error(\"DBX MCP process is not available\");\n    }\n    child.stdin.write(`${JSON.stringify(message)}\\n`);\n  };\n\n  const request = (method, params = {}, signal) =>\n    new Promise((resolve, reject) => {\n      const id = String(nextId++);\n      let onAbort;\n      const cleanup = () => {\n        clearTimeout(timer);\n        if (signal && onAbort) signal.removeEventListener(\"abort\", onAbort);\n      };\n      const resolveRequest = (value) => {\n        cleanup();\n        resolve(value);\n      };\n      const rejectRequest = (error) => {\n        cleanup();","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-core/assets/pi-mcp-bridge.mjs#L93-L129","documentation":"send() throws when the bridge is marked closed or the child MCP process's stdin is no longer writable, i.e. the subprocess has exited or its pipes are broken. Any request/send attempted afterwards fails immediately instead of hanging.","triggerScenarios":"Calling request()/send() after the child process exited (crash, non-zero exit, killed); racing startup before stdin is writable; a tool call issued after the readiness/closed lifecycle ended.","commonSituations":"MCP server crashed on startup (bad args, missing module); child killed by OOM or signal; tool invoked after teardown; slow startup with caller racing the ready file.","solutions":["Check the child's stderr for the actual crash cause and fix the MCP server startup (program path, args, dependencies)","Recreate/restart the bridge (respawn child) before sending further messages","Serialize calls behind the readiness signal; don't send before the ready file appears and don't reuse the bridge after 'closed'"],"exampleFix":"// before\nawait bridge.request(\"tools/call\", params); // child already exited\n// after\nif (!bridge.isAlive()) await restartBridge();\nawait bridge.request(\"tools/call\", params);","handlingStrategy":"try-catch","validationCode":"function canSend() {\n  return !closed && child && child.stdin && child.stdin.writable;\n}\nif (!canSend()) await restartBridge();","typeGuard":"function isBridgeAlive(bridge) {\n  return bridge && !bridge.closed && bridge.child?.stdin?.writable === true;\n}","tryCatchPattern":"try {\n  await request('tools/call', params, signal);\n} catch (e) {\n  if (e.message === 'DBX MCP process is not available') {\n    await respawnChild(); // inspect child stderr for crash cause\n    return retryOnce();\n  }\n  throw e;\n}","preventionTips":["Wait for the ready file before first send","Watch child exit/stderr and restart on death","Never reuse a bridge after it is closed","Catch child 'exit' events before issuing requests"],"tags":["ipc","process-lifecycle","mcp","broken-pipe"],"backgroundTag":"broken-pipe","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}