Skip to content

Commit f4f7774

Browse files
committed
fix(gui): gate the client-config fetch behind an explicit cancel flag
React Doctor flagged the setter after `await` in the panel's effect. The write was already safe — every result carries its request key and a superseded run's key no longer matches — but the guard was an `AbortController.signal.aborted` check, which states "the request was cancelled", not "this run may no longer write". An explicit `cancelled` flag set in the cleanup makes the invariant local to the setter, so a reader (and the linter) can see why a stale resolve cannot land. The abort stays: it stops the in-flight request, the flag stops the write.
1 parent be4e1ed commit f4f7774

1 file changed

Lines changed: 11 additions & 3 deletions

File tree

gui/src/components/apikeys-workspace/ClientConfigPanel.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,30 @@ export default function ClientConfigPanel({
6262

6363
useEffect(() => {
6464
const controller = new AbortController();
65+
// `cancelled` is what actually gates the setters. The abort signal alone left the
66+
// guard implicit — a reader (and react-doctor's no-set-state-after-await-in-effect)
67+
// cannot tell from the setter call that a superseded run can no longer write. Both
68+
// stay: abort stops the in-flight request, this flag stops the write.
69+
let cancelled = false;
6570
void (async () => {
6671
try {
6772
const res = await fetch(`${apiBase}/api/client-config?client=${encodeURIComponent(client)}`, {
6873
signal: controller.signal,
6974
});
7075
if (!res.ok) throw new Error(await apiErrorMessage(res, t("api.clientConfig.loadFailed")));
7176
const envelope = await res.json() as ClientConfigEnvelope;
72-
if (controller.signal.aborted) return;
77+
if (cancelled) return;
7378
setResult({ key: requestKey, data: envelope, error: null });
7479
} catch (cause) {
75-
if (controller.signal.aborted) return;
80+
if (cancelled) return;
7681
const message = cause instanceof Error && cause.message ? cause.message : t("api.clientConfig.loadFailed");
7782
setResult({ key: requestKey, data: null, error: message });
7883
}
7984
})();
80-
return () => controller.abort();
85+
return () => {
86+
cancelled = true;
87+
controller.abort();
88+
};
8189
}, [apiBase, client, requestKey, t]);
8290

8391
const settled = result !== null && result.key === requestKey ? result : null;

0 commit comments

Comments
 (0)