Compare commits

...

3 Commits

Author SHA1 Message Date
continue[bot]
b510535e7c refactor: improve retry condition clarity
Make the retry logic more explicit by checking isLastAttempt instead of
redundant comparison with OVERLOADED_RETRIES. This makes it clear that
we retry on overloaded errors unless we're on the final attempt.

Co-authored-by: nate <nate@continue.dev>

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2025-12-16 01:05:50 +00:00
continue[bot]
8b97c162ed fix: add missing cancelStream call and return for non-retryable errors
- Add cancelStream() call before showing error dialog for non-retryable errors
- Add return statement to prevent loop continuation after handling non-retryable error

Co-authored-by: nate <nate@continue.dev>

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2025-12-09 14:06:27 +00:00
uinstinct
2fccf3a3be feat: retry overloaded errors 2025-12-09 19:30:18 +05:30

View File

@@ -8,43 +8,63 @@ import { ThunkApiType } from "../store";
import { cancelStream } from "./cancelStream";
import { saveCurrentSession } from "./session";
const OVERLOADED_RETRIES = 3;
const OVERLOADED_DELAY_MS = 1000;
function isOverloadedErrorMessage(message?: string | null): boolean {
if (!message) return false;
const lower = message.toLowerCase();
return lower.includes("overloaded") || lower.includes("malformed json");
}
export const streamThunkWrapper = createAsyncThunk<
void,
() => Promise<void>,
ThunkApiType
>("chat/streamWrapper", async (runStream, { dispatch, extra, getState }) => {
try {
await runStream();
const state = getState();
if (!state.session.isInEdit) {
await dispatch(
saveCurrentSession({
openNewSession: false,
generateTitle: true,
}),
);
>("chat/streamWrapper", async (runStream, { dispatch, getState }) => {
for (let attempt = 0; attempt <= OVERLOADED_RETRIES; attempt++) {
try {
await runStream();
const state = getState();
if (!state.session.isInEdit) {
await dispatch(
saveCurrentSession({
openNewSession: false,
generateTitle: true,
}),
);
}
return;
} catch (e) {
// Get the selected model from the state for error analysis
const state = getState();
const selectedModel = selectSelectedChatModel(state);
const { parsedError, statusCode, message, modelTitle, providerName } =
analyzeError(e, selectedModel);
const isLastAttempt = attempt === OVERLOADED_RETRIES;
const shouldRetry = isOverloadedErrorMessage(message) && !isLastAttempt;
if (shouldRetry) {
await dispatch(cancelStream());
const delayMs = OVERLOADED_DELAY_MS * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
await dispatch(cancelStream());
} else {
await dispatch(cancelStream());
dispatch(setDialogMessage(<StreamErrorDialog error={e} />));
dispatch(setShowDialog(true));
const errorData = {
error_type: statusCode ? `HTTP ${statusCode}` : "Unknown",
error_message: parsedError,
model_provider: providerName,
model_title: modelTitle,
};
posthog.capture("gui_stream_error", errorData);
return;
}
}
} catch (e) {
await dispatch(cancelStream());
dispatch(setDialogMessage(<StreamErrorDialog error={e} />));
dispatch(setShowDialog(true));
// Get the selected model from the state for error analysis
const state = getState();
const selectedModel = selectSelectedChatModel(state);
const { parsedError, statusCode, modelTitle, providerName } = analyzeError(
e,
selectedModel,
);
const errorData = {
error_type: statusCode ? `HTTP ${statusCode}` : "Unknown",
error_message: parsedError,
model_provider: providerName,
model_title: modelTitle,
};
posthog.capture("gui_stream_error", errorData);
}
});