fix(opencode): cap session retries with jitter (#41939)

This commit is contained in:
Aiden Cline
2026-08-11 22:50:52 -05:00
committed by GitHub
parent d470434746
commit c78986831c
2 changed files with 46 additions and 7 deletions
+35 -4
View File
@@ -35,10 +35,18 @@ function wrap(message: unknown): ReturnType<NamedError["toObject"]> {
describe("session.retry.delay", () => {
test("caps delay at 30 seconds when headers missing", () => {
const error = apiError()
const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(index + 1, error))
const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(index + 1, error, 0))
expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 30000, 30000, 30000, 30000, 30000, 30000])
})
test("adds jitter to exponential delays", () => {
const error = apiError()
expect(SessionRetry.delay(1, error, 0)).toBe(2000)
expect(SessionRetry.delay(1, error, 1)).toBe(2500)
expect(SessionRetry.delay(4, error, 1)).toBe(20000)
expect(SessionRetry.delay(5, error, 1)).toBe(30000)
})
test("prefers retry-after-ms when shorter than exponential", () => {
const error = apiError({ "retry-after-ms": "1500" })
expect(SessionRetry.delay(4, error)).toBe(1500)
@@ -59,18 +67,18 @@ describe("session.retry.delay", () => {
test("ignores invalid retry hints", () => {
const error = apiError({ "retry-after": "not-a-number" })
expect(SessionRetry.delay(1, error)).toBe(2000)
expect(SessionRetry.delay(1, error, 0)).toBe(2000)
})
test("ignores malformed date retry hints", () => {
const error = apiError({ "retry-after": "Invalid Date String" })
expect(SessionRetry.delay(1, error)).toBe(2000)
expect(SessionRetry.delay(1, error, 0)).toBe(2000)
})
test("ignores past date retry hints", () => {
const pastDate = new Date(Date.now() - 5000).toUTCString()
const error = apiError({ "retry-after": pastDate })
expect(SessionRetry.delay(1, error)).toBe(2000)
expect(SessionRetry.delay(1, error, 0)).toBe(2000)
})
test("uses retry-after values even when exceeding 10 minutes with headers", () => {
@@ -115,6 +123,29 @@ describe("session.retry.delay", () => {
})
}),
)
it.instance("policy stops after five retries", () =>
Effect.gen(function* () {
const attempts: number[] = []
const error = apiError({ "retry-after-ms": "0" })
const step = yield* Schedule.toStepWithMetadata(
SessionRetry.policy({
provider: "test",
parse: Schema.decodeUnknownSync(SessionV1.APIError.Schema),
set: (info) =>
Effect.sync(() => {
attempts.push(info.attempt)
}),
}),
)
yield* Effect.forEach(Array.from({ length: SessionRetry.RETRY_MAX_RETRIES + 1 }), () =>
Effect.ignore(step(error)),
)
expect(attempts).toStrictEqual([1, 2, 3, 4, 5])
}),
)
})
describe("session.retry.retryable", () => {