{"record":{"id":"cac109133738ad42","repo":"iOfficeAI/OfficeCLI","slug":"1","errorCode":"-1","errorMessage":"resident is running but the command could not be delivered (pipe busy or unresponsive); retry, or close and reopen [${e.message}]","messagePattern":"resident is running but the command could not be delivered \\(pipe busy or unresponsive\\); retry, or close and reopen \\[(.+?)\\]","errorType":"error_code","errorClass":"OfficeCliError","httpStatus":null,"severity":"error","filePath":"sdk/node/index.js","lineNumber":208,"sourceCode":" * retries with backoff, then a blocking read. A retry only re-attempts the\n * connect (before the command runs), so it never double-applies a mutation. If\n * the command still can't be delivered, raise a busy/unresponsive error — never\n * fall back to touching the file directly (that would race the resident).\n *\n * `maxRetries` overrides the busy-retry count. Liveness probes (serves) pass 0\n * so a missing/stale pipe fails FAST instead of sleeping through the backoff.\n */\nasync function rpc(sockPath, req, connectTimeoutMs = BUSY_CONNECT_TIMEOUT_MS, maxRetries = BUSY_MAX_RETRIES) {\n  const line = Buffer.from(JSON.stringify(req) + '\\n', 'utf8');\n  let raw = null;\n  for (let attempt = 0; ; attempt++) {\n    try {\n      raw = await sendOnce(sockPath, line, connectTimeoutMs);\n      break;\n    } catch (e) {\n      // Connect/socket error only — the command never ran, so a retry is safe.\n      if (attempt >= maxRetries) {\n        throw new OfficeCliError(\n          -1,\n          'resident is running but the command could not be delivered ' +\n            `(pipe busy or unresponsive); retry, or close and reopen [${e.message}]`\n        );\n      }\n      await sleep(50 * (attempt + 1)); // = TrySend's 50*(n+1)ms backoff\n    }\n  }\n  const text = decodeLine(raw);\n  if (!text.trim()) {\n    // Empty/closed reply: the resident accepted the connection but closed\n    // without a complete response (e.g. crashed mid-serve). We refuse to\n    // re-send — the command may already have been APPLIED before the resident\n    // died, so re-sending would double-apply a non-idempotent op — and raise\n    // instead. _cmd's recovery then restarts a dead resident and retries once.\n    throw new OfficeCliError(\n      -1,\n      'resident closed the connection without a response ' +","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/sdk/node/index.js#L190-L226","documentation":"Raised by rpc() (code -1) when the resident's pipe cannot be connected to / written within BUSY_MAX_RETRIES (3) attempts with backoff, mirroring officecli's TrySend busy-delivery policy. Because every retry happens BEFORE the command executes, re-sending is safe; this error means even the generous connect budget (30s) was exhausted. The message embeds the underlying socket error.","triggerScenarios":"The resident is alive but saturated: a long-running command holds the serialized queue and all connect attempts time out; too many concurrent SDK clients contending for one named pipe; the OS pipe backlog is full; transient ECONNREFUSED/EAGAIN while the resident is mid-handshake.","commonSituations":"A large batch over a slow doc; many parallel workers sharing one resident; a resident stuck in a slow save; the pipe briefly unavailable during a restart window.","solutions":["Simply retry the call (send/batch) — the failure occurred before the command ran, so a retry cannot double-apply.","Reduce concurrency against the shared resident (serialize or queue your sends) so the connect budget is not exhausted.","Call doc.close() then oc.open() again to restart a resident that appears wedged.","Raise the per-call timeout via the timeoutMs argument if the resident is legitimately busy for long periods."],"exampleFix":"// before: doc.send(...) -> [exit -1] resident is running but the command could not be delivered\n// after: retry idempotently with backoff\nasync function sendRetry(doc, item, tries = 4) {\n  for (let i = 0; i < tries; i++) {\n    try { return await doc.send(item); }\n    catch (e) { if (e.code !== -1 || i === tries - 1) throw e; await new Promise(r => setTimeout(r, 100 * (i + 1))); }\n  }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Safe retry: the failure happens BEFORE the command runs, so re-send cannot double-apply\nasync function sendResilient(doc, item, { tries = 4, base = 100 } = {}) {\n  for (let i = 0; ; i++) {\n    try { return await doc.send(item); }\n    catch (e) {\n      const busy = e.code === -1 && /could not be delivered/.test(e.message);\n      if (!busy || i >= tries - 1) throw e;\n      await new Promise(r => setTimeout(r, base * (i + 1)));\n    }\n  }\n}","preventionTips":["Serialize or bound concurrency against one resident (the pipe is a serialized queue).","Pass a generous timeoutMs for legitimately long operations.","Retry on code -1 'could not be delivered' — it is pre-execution and idempotent to retry."],"tags":["transport","pipe","resident","retry","concurrency","timeout"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}