Merge reviewed credential contention handling into accounting

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Devin FoleyandPaperclip committed 2026-10-04 01:03:53 -07:00
commit 3f8a95f8a4
5 files changed
+81 -7

No files matched your search

+13
View File
@@ -1,3 +1,4 @@
import { WORKSPACE_RESTORE_LOCK_TIMEOUT_CODE } from "@paperclipai/adapter-utils/workspace-restore-merge";
import { withAccountHomeSecretMutationLock } from "@paperclipai/adapter-codex-local/server";
import { syncConnectionCredentialBindings } from "./connection-credential-bindings.js";
import { createHash, randomUUID } from "node:crypto";
@@ -861,6 +862,18 @@ export function aiConnectionService(db: Db) {
if (recovered) await quotaCredentialRecovery(row.connection.companyId, row.grant.id).clear()
.catch(error => logger.warn({ err: error, grantId: row.grant.id }, "Saved runtime credential recovery cleanup pending"));
return value;
}).catch(error => {
// Drizzle wraps Postgres errors in `cause`. Contention is a temporary
// pre-provider wait, not evidence that this account needs reconnecting.
let current: unknown = error;
for (let depth = 0; depth < 4 && current && typeof current === "object"; depth += 1) {
const value = current as { code?: unknown; cause?: unknown };
if (value.code === WORKSPACE_RESTORE_LOCK_TIMEOUT_CODE || value.code === "55P03") {
throw unprocessable("AI credentials are being updated. This execution will retry automatically.", { code: "ai_connection_busy" });
}
current = value.cause;
}
throw error;
});
}
+2 -3
View File
@@ -16375,9 +16375,8 @@ export function heartbeatService(
.then((rows) => rows[0] ?? null);
}
// No code path raises the `ai_connection_busy` error any more. This
// function stays because a stored run row can still carry that error code
// from an earlier release.
// Credential rotation can briefly contend with a fresh runtime read. Keep
// the task on its automatic pre-provider retry path while the lock clears.
async function finalizeAiConnectionBusyDeferral(
run: typeof heartbeatRuns.$inferSelect,
error: HttpError,