Q61: a failed status check no longer ends recovery of a background response

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 10:42:57 -04:00
1 parent 88463e4396
commit 46cd167c2c
6 files changed
+93 -2

No files matched your search

@@ -0,0 +1,7 @@
export const backgroundPollFailuresSchema = `
alter table provider_requests add column poll_failures integer not null default 0;
alter table provider_requests add column last_poll_error text;
insert into schema_migrations(version, name, applied_at)
values (71, 'background_poll_failures', datetime('now'));
`;
+2
View File
@@ -69,6 +69,7 @@ import { taskRunBudgetSchema } from "./migrations/067-task-run-budget";
import { taskReviewLinkSchema } from "./migrations/068-task-review-link";
import { goalStallSchema } from "./migrations/069-goal-stall";
import { runActionsSchema } from "./migrations/070-run-actions";
import { backgroundPollFailuresSchema } from "./migrations/071-background-poll-failures";
interface Migration {
version: number;
@@ -147,6 +148,7 @@ const MIGRATIONS: Migration[] = [
{ version: 68, name: "task_review_link", sql: taskReviewLinkSchema },
{ version: 69, name: "goal_stall", sql: goalStallSchema },
{ version: 70, name: "run_actions", sql: runActionsSchema },
{ version: 71, name: "background_poll_failures", sql: backgroundPollFailuresSchema },
];
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
@@ -24,6 +24,7 @@ const AUTH_STATE_CONFIGURED = "configured";
const HEALTH_STATE_HEALTHY = "healthy";
const ACTIVE_BACKGROUND_STATUSES = new Set(["queued", "in_progress"]);
const FAILED_BACKGROUND_STATUSES = new Set(["failed", "incomplete"]);
const MAX_POLL_FAILURES = 10;
const CANCELLED_BACKGROUND_STATUSES = new Set(["cancelled"]);
type BackgroundCreateInput = CreateProviderResponseInput & {
@@ -137,6 +138,7 @@ export class ProviderBackgroundService {
});
this.recordObservedRateLimit(request.org_id, providerRow, providerRow.model, result.rateLimitState);
if (ACTIVE_BACKGROUND_STATUSES.has(result.status)) {
this.requestStore.clearPollFailures(request.id);
this.requestStore.markBackgroundProgress({
orgId: request.org_id,
requestId: request.id,
@@ -149,6 +151,7 @@ export class ProviderBackgroundService {
}
if (result.status === "completed") {
const responseText = requireBackgroundText(result.text, provider.provider_key);
this.requestStore.clearPollFailures(request.id);
this.requestStore.markSucceeded({
orgId: request.org_id,
requestId: request.id,
@@ -170,6 +173,7 @@ export class ProviderBackgroundService {
return;
}
if (CANCELLED_BACKGROUND_STATUSES.has(result.status)) {
this.requestStore.clearPollFailures(request.id);
this.requestStore.markCancelled({
orgId: request.org_id,
requestId: request.id,
@@ -185,6 +189,15 @@ export class ProviderBackgroundService {
}
throw new Error(`Provider background response returned unsupported status ${result.status}.`);
} catch (error) {
// The provider reporting failure is final; a failed status check is not, since the remote job may still finish.
let finalError = error;
if (!(error instanceof ProviderBackgroundStatusError)) {
const message = error instanceof Error ? error.message : String(error);
const failures = this.requestStore.recordPollFailure(request.id, message);
console.warn(`[provider-background] status check ${failures}/${MAX_POLL_FAILURES} for request ${request.id} failed: ${message}`);
if (failures < MAX_POLL_FAILURES) return;
finalError = new Error(`Gave up after ${MAX_POLL_FAILURES} failed status checks: ${message}`);
}
this.responseObserver.recordFailure({
orgId: request.org_id,
provider,
@@ -192,7 +205,7 @@ export class ProviderBackgroundService {
executionMode: PROVIDER_EXECUTION_MODE.BACKGROUND,
startedAt,
inputCharCount: request.input_char_count,
error,
error: finalError,
rawResponseJson: error instanceof ProviderBackgroundStatusError ? error.rawResponseJson : null,
});
}
@@ -156,6 +156,16 @@ export class ProviderRequestStore {
);
}
/** Counts a failed status check on a background request and returns how many happened in a row. */
recordPollFailure(requestId: string, message: string): number {
this.db.prepare("update provider_requests set poll_failures = poll_failures + 1, last_poll_error = ? where id = ?").run(message, requestId);
return (this.db.prepare("select poll_failures from provider_requests where id = ?").get(requestId) as { poll_failures: number }).poll_failures;
}
clearPollFailures(requestId: string): void {
this.db.prepare("update provider_requests set poll_failures = 0, last_poll_error = null where id = ?").run(requestId);
}
markBackgroundProgress(input: ProviderBackgroundProgressInput): void {
this.db.prepare(`
update provider_requests