Skip to content

Commit 44304ba

Browse files
joeykrugsteipete
authored andcommitted
fix: add retry with exponential backoff for orphan recovery
Addresses Codex review feedback β€” if recovery fails (e.g. gateway still booting), retries up to 3 times with exponential backoff (5s β†’ 10s β†’ 20s) before giving up.
1 parent 0311ff0 commit 44304ba

1 file changed

Lines changed: 40 additions & 7 deletions

File tree

β€Žsrc/agents/subagent-orphan-recovery.tsβ€Ž

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,51 @@ export async function recoverOrphanedSubagentSessions(params: {
174174
return result;
175175
}
176176

177+
/** Maximum number of retry attempts for orphan recovery. */
178+
const MAX_RECOVERY_RETRIES = 3;
179+
/** Backoff multiplier between retries (exponential). */
180+
const RETRY_BACKOFF_MULTIPLIER = 2;
181+
177182
/**
178-
* Schedule orphan recovery after a delay.
183+
* Schedule orphan recovery after a delay, with retry logic.
179184
* The delay gives the gateway time to fully bootstrap after restart.
185+
* If recovery fails (e.g. gateway not yet ready), retries with exponential backoff.
180186
*/
181187
export function scheduleOrphanRecovery(params: {
182188
getActiveRuns: () => Map<string, SubagentRunRecord>;
183189
delayMs?: number;
190+
maxRetries?: number;
184191
}): void {
185-
const delay = params.delayMs ?? DEFAULT_RECOVERY_DELAY_MS;
186-
setTimeout(() => {
187-
void recoverOrphanedSubagentSessions(params).catch((err) => {
188-
log.warn(`scheduled orphan recovery failed: ${String(err)}`);
189-
});
190-
}, delay).unref?.();
192+
const initialDelay = params.delayMs ?? DEFAULT_RECOVERY_DELAY_MS;
193+
const maxRetries = params.maxRetries ?? MAX_RECOVERY_RETRIES;
194+
195+
const attemptRecovery = (attempt: number, delay: number) => {
196+
setTimeout(() => {
197+
void recoverOrphanedSubagentSessions(params)
198+
.then((result) => {
199+
if (result.failed > 0 && attempt < maxRetries) {
200+
const nextDelay = delay * RETRY_BACKOFF_MULTIPLIER;
201+
log.info(
202+
`orphan recovery had ${result.failed} failure(s); retrying in ${nextDelay}ms (attempt ${attempt + 1}/${maxRetries})`,
203+
);
204+
attemptRecovery(attempt + 1, nextDelay);
205+
}
206+
})
207+
.catch((err) => {
208+
if (attempt < maxRetries) {
209+
const nextDelay = delay * RETRY_BACKOFF_MULTIPLIER;
210+
log.warn(
211+
`scheduled orphan recovery failed: ${String(err)}; retrying in ${nextDelay}ms (attempt ${attempt + 1}/${maxRetries})`,
212+
);
213+
attemptRecovery(attempt + 1, nextDelay);
214+
} else {
215+
log.warn(
216+
`scheduled orphan recovery failed after ${maxRetries} retries: ${String(err)}`,
217+
);
218+
}
219+
});
220+
}, delay).unref?.();
221+
};
222+
223+
attemptRecovery(0, initialDelay);
191224
}

0 commit comments

Comments
Β (0)