paperclipai/paperclip · warning
OpenCode request ${input.requestId} is already settling
Error message
OpenCode request ${input.requestId} is already settling What it means
Once `resolveRuntimeRequest` starts submitting a reply/reject to OpenCode, it sets `pending.settling = true`; a second concurrent resolution attempt for the same requestId while the first HTTP call is still in flight throws this error. This guards against racing duplicate replies (OpenCode's question/permission reply APIs are not idempotent). The flag is reset only if the submitting operation itself throws.
Source
Thrown at packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts:643
if (!pending)
throw new Error(
`OpenCode request ${input.requestId} is no longer pending`,
);
if (
pending.request.turnId !== input.turnId ||
this.#activeTurnId !== input.turnId
) {
throw new Error(
`OpenCode request ${input.requestId} belongs to a stale turn`,
);
}
const resolution = parseHarnessRuntimeRequestResolution(
pending.request.requestKind,
input.resolution,
pending.request.input,
);
if (pending.settling)
throw new Error(
`OpenCode request ${input.requestId} is already settling`,
);
pending.settling = true;
const submit = async (operation: Promise<unknown>) => {
try {
await operation;
} catch (error) {
if (this.#pendingRuntimeRequests.get(input.requestId) === pending)
pending.settling = false;
throw error;
}
};
const workspace = `directory=${encodeURIComponent(this.#workingDirectory)}`;
if (pending.request.requestKind === "permission_approval") {
const action =
resolution.action === "accept" ||
resolution.action === "accept_for_session"
? resolution.actionView on GitHub (pinned to 01ad858492)
Solutions
- Serialize resolutions per requestId (single-flight: keep a map of in-flight promises and reuse the same promise).
- On catching this error, await the original in-flight resolution instead of re-calling; the first call will emit `runtime_request.resolved`.
- Wait for the `runtime_request.resolved` (or `.expired`/`.cancelled`) event for the requestId before considering it resolvable again.
- If the original submit threw, `settling` resets to false — retry only after observing the thrown error from the original caller.
Example fix
// before
await Promise.all([
session.resolveRuntimeRequest({ requestId, turnId, resolution }),
session.resolveRuntimeRequest({ requestId, turnId, resolution }), // throws 'already settling'
]);
// after
const inflight = new Map();
function resolveOnce(input) {
if (!inflight.has(input.requestId)) {
inflight.set(input.requestId,
session.resolveRuntimeRequest(input).finally(() => inflight.delete(input.requestId)));
}
return inflight.get(input.requestId);
}
await Promise.all([resolveOnce({ requestId, turnId, resolution }), resolveOnce({ requestId, turnId, resolution })]); Defensive patterns
Strategy: try-catch
Validate before calling
if (inflightResolutions.has(requestId)) return inflightResolutions.get(requestId);
Type guard
function isSettling(session, requestId) {
return session.pendingRuntimeRequests().length > 0; // cannot observe settling directly; use single-flight instead
} Try / catch
try {
await session.resolveRuntimeRequest({ requestId, turnId, resolution });
} catch (e) {
if (e.message.includes('is already settling')) {
await inflightResolutions.get(requestId); // await the original attempt
} else throw e;
} Prevention
- Serialize resolutions with a single-flight promise map per requestId.
- Disable UI controls once a resolution is submitted.
- Wait for `runtime_request.resolved` events instead of polling.
- Avoid timeout-based retries that overlap an in-flight reply.
When it happens
Trigger: Two concurrent `resolveRuntimeRequest` calls for the same requestId — e.g. user double-clicks Approve, a retry timer fires while the first request awaits `api(...)`, or an auto-resolver and a manual resolver race.
Common situations: Slow OpenCode server makes the first reply take seconds, inviting a timeout-based retry that collides; parallel promise chains both responding to the same native question; a supervisor and the UI both answering a permission prompt.
Related errors
- OpenCode request ${input.requestId} is no longer pending
- opencode_run_attach_busy
- OpenCode session already has an active turn
- OpenCode request ${input.requestId} belongs to a stale turn
- OpenCode evals require exact version 1.18.17; received ${ver
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/fd63a5871134ad2e.
Report an issue: GitHub.