Skip to content

Commit 0e30e41

Browse files
committed
fix: retry auth token delivery to chat iframe with backoff
When the chat iframe sends coder:vscode-ready, the session token may not be available yet if deployment setup is still in progress after a reload. Previously, handleMessage silently returned, leaving the iframe stuck on 'Authenticating...' forever with no recovery. Now sendAuthToken retries up to 5 times with exponential backoff (500ms, 1s, 2s, 4s, 8s). If the token is still unavailable after all retries, a coder:auth-error message is sent to the webview, which shows the error and a Retry button so the user can try again after signing in.
1 parent 743ffa8 commit 0e30e41

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

.vscode-test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineConfig } from "@vscode/test-cli";
22

33
// VS Code to Electron/Node version mapping:
4-
// VS Code 1.106 (Jun 2025) -> Node 22 - Minimum supported
4+
// VS Code 1.106 (Oct 2025) -> Node 22 - Minimum supported
55
// VS Code stable -> Latest
66
const versions = ["1.106.0", "stable"];
77

src/webviews/chat/chatPanelProvider.ts

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,46 @@ export class ChatPanelProvider
9494
}
9595
const msg = message as { type?: string };
9696
if (msg.type === "coder:vscode-ready") {
97-
const token = this.client.getSessionToken();
98-
if (!token) {
99-
this.logger.warn(
100-
"Chat iframe requested auth but no session token available",
97+
this.sendAuthToken();
98+
}
99+
}
100+
101+
/**
102+
* Attempt to forward the session token to the chat iframe.
103+
* The token may not be available immediately after a reload
104+
* (e.g. deployment setup is still in progress), so we retry
105+
* with exponential backoff before giving up.
106+
*/
107+
private static readonly MAX_AUTH_RETRIES = 5;
108+
private static readonly AUTH_RETRY_BASE_MS = 500;
109+
110+
private sendAuthToken(attempt = 0): void {
111+
const token = this.client.getSessionToken();
112+
if (!token) {
113+
if (attempt < ChatPanelProvider.MAX_AUTH_RETRIES) {
114+
const delay = ChatPanelProvider.AUTH_RETRY_BASE_MS * 2 ** attempt;
115+
this.logger.info(
116+
`Chat: no session token yet, retrying in ${delay}ms ` +
117+
`(attempt ${attempt + 1}/${ChatPanelProvider.MAX_AUTH_RETRIES})`,
101118
);
119+
setTimeout(() => this.sendAuthToken(attempt + 1), delay);
102120
return;
103121
}
104-
this.logger.info("Chat: forwarding token to iframe");
122+
this.logger.warn(
123+
"Chat iframe requested auth but no session token available " +
124+
"after all retries",
125+
);
105126
this.view?.webview.postMessage({
106-
type: "coder:auth-bootstrap-token",
107-
token,
127+
type: "coder:auth-error",
128+
error: "No session token available. Please sign in and retry.",
108129
});
130+
return;
109131
}
132+
this.logger.info("Chat: forwarding token to iframe");
133+
this.view?.webview.postMessage({
134+
type: "coder:auth-bootstrap-token",
135+
token,
136+
});
110137
}
111138

112139
private getIframeHtml(embedUrl: string, allowedOrigin: string): string {
@@ -136,6 +163,17 @@ export class ChatPanelProvider
136163
font-family: var(--vscode-font-family, sans-serif);
137164
font-size: 13px; padding: 16px; text-align: center;
138165
}
166+
#retry-btn {
167+
margin-top: 12px; padding: 6px 16px;
168+
background: var(--vscode-button-background, #0e639c);
169+
color: var(--vscode-button-foreground, #fff);
170+
border: none; border-radius: 2px; cursor: pointer;
171+
font-family: var(--vscode-font-family, sans-serif);
172+
font-size: 13px;
173+
}
174+
#retry-btn:hover {
175+
background: var(--vscode-button-hoverBackground, #1177bb);
176+
}
139177
</style>
140178
</head>
141179
<body>
@@ -172,6 +210,22 @@ export class ChatPanelProvider
172210
payload: { token: data.token },
173211
}, '${allowedOrigin}');
174212
}
213+
214+
if (data.type === 'coder:auth-error') {
215+
status.innerHTML = '';
216+
status.appendChild(document.createTextNode(data.error || 'Authentication failed.'));
217+
const btn = document.createElement('button');
218+
btn.id = 'retry-btn';
219+
btn.textContent = 'Retry';
220+
btn.addEventListener('click', () => {
221+
status.textContent = 'Authenticating…';
222+
vscode.postMessage({ type: 'coder:vscode-ready' });
223+
});
224+
status.appendChild(document.createElement('br'));
225+
status.appendChild(btn);
226+
status.style.display = 'block';
227+
iframe.style.display = 'none';
228+
}
175229
});
176230
})();
177231
</script>

0 commit comments

Comments
 (0)