iOfficeAI/OfficeCLI · error · OfficeCliError
-1
-1
Error message
resident is running but the command could not be delivered (pipe busy or unresponsive); retry, or close and reopen [{e}] What it means
Raised by the Python SDK (code -1) when sending a command to the resident pipe fails with OSError on every attempt up to max_retries+1. The resident is running (the connect succeeded) but the pipe is busy or unresponsive during delivery. The SDK retries with 50*(n+1)ms backoff before giving up; liveness probes (_serves) pass max_retries=0 so a stale pipe fails fast instead of sleeping.
Source
Thrown at sdk/python/officecli.py:224
"""Forward one request, mirroring officecli's TrySend: bounded connect + a few
retries with backoff, then a blocking read. A retry only re-attempts the
connect (before the command runs), so it never double-applies a mutation. If
the command still can't be delivered, raise a busy/unresponsive error — never
fall back to touching the file directly (that would race the resident).
`max_retries` overrides the busy-retry count. Liveness probes (_serves) pass 0
so a missing/stale pipe fails FAST instead of sleeping through ~0.3s of backoff
— retrying a probe the resident isn't answering can't make it answer; the
busy-retry policy is for delivering a real command to a slow-but-live pipe."""
line = (json.dumps(req, ensure_ascii=False) + "\n").encode("utf-8")
send = _send_win if _IS_WIN else _send_unix
for attempt in range(max_retries + 1):
try:
raw = send(sock_path, line, connect_timeout)
break
except OSError as e:
if attempt >= max_retries:
raise OfficeCliError(-1,
f"resident is running but the command could not be delivered "
f"(pipe busy or unresponsive); retry, or close and reopen [{e}]")
time.sleep(0.05 * (attempt + 1)) # = TrySend's 50*(n+1)ms backoff
# utf-8-sig: the resident's StreamWriter (Encoding.UTF8) prepends a BOM the
# C# StreamReader strips; we must too, or json.loads chokes on the leading .
text = raw.decode("utf-8-sig")
if not text.strip():
# Empty/closed reply: the resident accepted the connection but closed
# without a complete response (e.g. crashed mid-serve). We refuse to
# re-send — the command may already have been APPLIED before the resident
# died, so re-sending would double-apply a non-idempotent op — and raise
# instead. officecli's TrySend now matches: its retry covers only the
# connect phase (before the command is written); on an empty reply after a
# successful write it returns null without re-sending, the C# equivalent of
# this raise. _cmd's recovery then restarts a dead resident and retries once
# (a fresh connect, before re-send), and _serves()/alive() (which swallow
# OfficeCliError) read an empty reply as "not alive".
raise OfficeCliError(-1,View on GitHub (pinned to 1ced45e900)
Solutions
- Retry the operation at the caller level with backoff — the failure is often transient.
- Reduce concurrency against the single resident, or batch writes to lower round-trips.
- Close and reopen the Document (restarts the resident) if it stays unresponsive.
Example fix
# before
doc.set('/Sheet1/A1', {'text': 'x'})
# after — caller retry with backoff
import time
for attempt in range(5):
try:
doc.set('/Sheet1/A1', {'text': 'x'}); break
except officecli.OfficeCliError as e:
if attempt == 4: raise
time.sleep(0.1 * (attempt + 1)) Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
import time
import officecli
def send_resilient(doc, fn, *args, retries=4, **kw):
last = None
for attempt in range(retries + 1):
try:
return fn(*args, **kw)
except officecli.OfficeCliError as e:
last = e
if 'could not be delivered' not in str(e) or attempt == retries:
raise
time.sleep(0.1 * (attempt + 1))
raise last Prevention
- Wrap deliveries in a caller-side retry with backoff for transient pipe contention.
- Lower concurrency against a single resident, or batch writes.
- Reopen the Document to restart an unresponsive resident.
When it happens
Trigger: High pipe contention from many concurrent SDK clients; a slow/hung resident that accepts connections but cannot drain commands; transient OS pipe errors under load. Surfaces after all configured retries are exhausted.
Common situations: Many threads/processes sharing one resident; a large batch blocking the resident so single commands time out on delivery; antivirus or sandbox interfering with the named pipe/socket.
Related errors
- -1
- file_locked
- Cannot read OLE source file '{srcPath}': the file is locked
- Another watch process is already running{url} for {_filePath
- 127
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/f18b08e50bed1584.
Report an issue: GitHub.