agent: @U0AJM7X8FBR currently, after a couple minutes of the chat app being ope#1579
agent: @U0AJM7X8FBR currently, after a couple minutes of the chat app being ope#1579recoup-coding-agent wants to merge 1 commit intotestfrom
Conversation
Privy access tokens expire, but the token was only fetched once on mount via useAccessToken and cached in React state. After expiry, subsequent chat requests were sent with the stale token and failed with 401 errors. Now getAccessToken() is called right before each sendMessage() call so Privy's SDK can transparently refresh the token if needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe chat hook now integrates Privy-based authentication for token refresh. Before sending messages, the hook retrieves a fresh access token via Changes
Sequence DiagramsequenceDiagram
participant Client as React Component
participant Hook as useVercelChat Hook
participant Privy as Privy Auth
participant API as Chat API
Client->>Hook: Call handleSubmit(message)
Hook->>Privy: getAccessToken()
Privy-->>Hook: Fresh access token
Hook->>Hook: Construct requestOptions with<br/>Authorization header
Hook->>API: POST message with token
API-->>Hook: Response
Hook-->>Client: Updated chat state
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ❌ 1❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae65b98fee
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| sendMessage(payload, chatRequestOptions); | ||
| // Get a fresh token at send time so expired tokens are never used | ||
| const freshToken = await getAccessToken(); |
There was a problem hiding this comment.
Mark send state before awaiting access token
Awaiting getAccessToken() before sendMessage() leaves the form in a sendable state while token refresh is in progress; when refresh takes noticeable time (the exact expiry case this patch targets), users can press Enter/click submit again and enqueue duplicate copies of the same prompt, causing duplicate model runs and credit usage. This path previously submitted immediately, so the new await introduces a regression unless resubmission is blocked before the token call.
Useful? React with 👍 / 👎.
| async (initialMessage: UIMessage) => { | ||
| silentlyUpdateUrl(); | ||
| sendMessage(initialMessage, chatRequestOptions); | ||
| const freshToken = await getAccessToken(); |
There was a problem hiding this comment.
Avoid re-triggering initial send during token refresh
The initial-message path now waits for getAccessToken() before calling sendMessage(), which creates a window where status is still ready and messages are unchanged; if a rerender updates dependencies (for example model initialization updates chatRequestOptions), the effect can invoke this callback again and send the initial prompt twice. This duplicate-send condition is specific to slower token retrieval and was not present when sendMessage() happened synchronously.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
hooks/useVercelChat.ts (1)
252-254:⚠️ Potential issue | 🟠 Major
appendfunction doesn't use fresh token pattern, creating inconsistency.The
appendfunction still useschatRequestOptionsdirectly without refreshing the token, whilehandleSubmitandhandleSendQueryMessagesnow fetch a fresh token before sending. This could lead to the same 401 errors the PR is fixing ifappendis called after token expiry.🔧 Proposed fix to make append consistent
- const append = (message: UIMessage) => { - sendMessage(message, chatRequestOptions); + const append = useCallback(async (message: UIMessage) => { + const freshToken = await getAccessToken(); + const requestOptions = freshToken + ? { + ...chatRequestOptions, + headers: { ...chatRequestOptions.headers, Authorization: `Bearer ${freshToken}` }, + } + : chatRequestOptions; + sendMessage(message, requestOptions); + }, [getAccessToken, chatRequestOptions, sendMessage]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/useVercelChat.ts` around lines 252 - 254, The append function uses the stale chatRequestOptions causing token-expiry 401s; update append (in hooks/useVercelChat.ts) to follow the same fresh-token pattern as handleSubmit and handleSendQueryMessages by calling getVercelEdgeToken() (or the existing token-refresh helper), merge the returned token into a new chatRequestOptions object, and then call sendMessage(message, updatedChatRequestOptions) so sendMessage always receives a current token.
🧹 Nitpick comments (1)
hooks/useVercelChat.ts (1)
239-248: Consider extracting token-refresh logic to reduce duplication.The pattern of fetching a fresh token and building
requestOptionsis repeated inhandleSubmit,handleSendQueryMessages, and should be added toappend. A small helper would centralize this logic.♻️ Optional helper extraction
// Add inside the hook, before the callbacks const buildRequestOptionsWithFreshToken = useCallback(async () => { const freshToken = await getAccessToken(); return freshToken ? { ...chatRequestOptions, headers: { ...chatRequestOptions.headers, Authorization: `Bearer ${freshToken}` }, } : chatRequestOptions; }, [getAccessToken, chatRequestOptions]);Then use it in each location:
const requestOptions = await buildRequestOptionsWithFreshToken(); sendMessage(payload, requestOptions);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hooks/useVercelChat.ts` around lines 239 - 248, Extract the repeated token-refresh + options-build logic into a single helper (e.g. buildRequestOptionsWithFreshToken) inside the hook and use it from handleSubmit, handleSendQueryMessages and append; the helper should call getAccessToken(), and if a token exists return { ...chatRequestOptions, headers: { ...chatRequestOptions.headers, Authorization: `Bearer ${token}` } } else return chatRequestOptions, and register it with useCallback (dependencies: getAccessToken, chatRequestOptions) before replacing the existing ad-hoc token-fetch + requestOptions construction and calling sendMessage(payload, requestOptions).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@hooks/useVercelChat.ts`:
- Around line 325-332: The current token-injection before calling sendMessage
overwrites any existing headers on chatRequestOptions; update the merge so
headers = { ...(chatRequestOptions.headers || {}), Authorization: `Bearer
${freshToken}` } when building requestOptions (inside the getAccessToken branch)
so existing headers are preserved—apply this change where getAccessToken,
chatRequestOptions, initialMessage and sendMessage are used.
- Around line 239-248: The code overwrites chatRequestOptions.headers when
freshToken exists, dropping any existing headers; fix by creating requestOptions
from chatRequestOptions (or an empty object) and set headers to a new object
that merges existing chatRequestOptions.headers with the Authorization header
(so Authorization wins but other headers are preserved), do not mutate
chatRequestOptions, and then pass this merged requestOptions into sendMessage;
reference getAccessToken, chatRequestOptions, freshToken, and sendMessage when
locating the change.
---
Outside diff comments:
In `@hooks/useVercelChat.ts`:
- Around line 252-254: The append function uses the stale chatRequestOptions
causing token-expiry 401s; update append (in hooks/useVercelChat.ts) to follow
the same fresh-token pattern as handleSubmit and handleSendQueryMessages by
calling getVercelEdgeToken() (or the existing token-refresh helper), merge the
returned token into a new chatRequestOptions object, and then call
sendMessage(message, updatedChatRequestOptions) so sendMessage always receives a
current token.
---
Nitpick comments:
In `@hooks/useVercelChat.ts`:
- Around line 239-248: Extract the repeated token-refresh + options-build logic
into a single helper (e.g. buildRequestOptionsWithFreshToken) inside the hook
and use it from handleSubmit, handleSendQueryMessages and append; the helper
should call getAccessToken(), and if a token exists return {
...chatRequestOptions, headers: { ...chatRequestOptions.headers, Authorization:
`Bearer ${token}` } } else return chatRequestOptions, and register it with
useCallback (dependencies: getAccessToken, chatRequestOptions) before replacing
the existing ad-hoc token-fetch + requestOptions construction and calling
sendMessage(payload, requestOptions).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 38042866-a837-4a5c-9989-8a56e81d527f
📒 Files selected for processing (1)
hooks/useVercelChat.ts
| // Get a fresh token at send time so expired tokens are never used | ||
| const freshToken = await getAccessToken(); | ||
| const requestOptions = freshToken | ||
| ? { | ||
| ...chatRequestOptions, | ||
| headers: { Authorization: `Bearer ${freshToken}` }, | ||
| } | ||
| : chatRequestOptions; | ||
|
|
||
| sendMessage(payload, requestOptions); |
There was a problem hiding this comment.
Headers are replaced instead of merged, potentially losing other headers.
When freshToken exists, the code overwrites headers entirely rather than merging with existing headers from chatRequestOptions. If chatRequestOptions.headers contains additional headers (now or in the future), they will be silently dropped.
🔧 Proposed fix to merge headers
// Get a fresh token at send time so expired tokens are never used
const freshToken = await getAccessToken();
const requestOptions = freshToken
? {
...chatRequestOptions,
- headers: { Authorization: `Bearer ${freshToken}` },
+ headers: { ...chatRequestOptions.headers, Authorization: `Bearer ${freshToken}` },
}
: chatRequestOptions;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Get a fresh token at send time so expired tokens are never used | |
| const freshToken = await getAccessToken(); | |
| const requestOptions = freshToken | |
| ? { | |
| ...chatRequestOptions, | |
| headers: { Authorization: `Bearer ${freshToken}` }, | |
| } | |
| : chatRequestOptions; | |
| sendMessage(payload, requestOptions); | |
| // Get a fresh token at send time so expired tokens are never used | |
| const freshToken = await getAccessToken(); | |
| const requestOptions = freshToken | |
| ? { | |
| ...chatRequestOptions, | |
| headers: { ...(chatRequestOptions.headers || {}), Authorization: `Bearer ${freshToken}` }, | |
| } | |
| : chatRequestOptions; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/useVercelChat.ts` around lines 239 - 248, The code overwrites
chatRequestOptions.headers when freshToken exists, dropping any existing
headers; fix by creating requestOptions from chatRequestOptions (or an empty
object) and set headers to a new object that merges existing
chatRequestOptions.headers with the Authorization header (so Authorization wins
but other headers are preserved), do not mutate chatRequestOptions, and then
pass this merged requestOptions into sendMessage; reference getAccessToken,
chatRequestOptions, freshToken, and sendMessage when locating the change.
| const freshToken = await getAccessToken(); | ||
| const requestOptions = freshToken | ||
| ? { | ||
| ...chatRequestOptions, | ||
| headers: { Authorization: `Bearer ${freshToken}` }, | ||
| } | ||
| : chatRequestOptions; | ||
| sendMessage(initialMessage, requestOptions); |
There was a problem hiding this comment.
Same headers merge issue applies here.
Consistent with the earlier comment on handleSubmit, this should also spread existing headers to avoid losing them.
🔧 Proposed fix
const freshToken = await getAccessToken();
const requestOptions = freshToken
? {
...chatRequestOptions,
- headers: { Authorization: `Bearer ${freshToken}` },
+ headers: { ...chatRequestOptions.headers, Authorization: `Bearer ${freshToken}` },
}
: chatRequestOptions;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const freshToken = await getAccessToken(); | |
| const requestOptions = freshToken | |
| ? { | |
| ...chatRequestOptions, | |
| headers: { Authorization: `Bearer ${freshToken}` }, | |
| } | |
| : chatRequestOptions; | |
| sendMessage(initialMessage, requestOptions); | |
| const freshToken = await getAccessToken(); | |
| const requestOptions = freshToken | |
| ? { | |
| ...chatRequestOptions, | |
| headers: { ...chatRequestOptions.headers, Authorization: `Bearer ${freshToken}` }, | |
| } | |
| : chatRequestOptions; | |
| sendMessage(initialMessage, requestOptions); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/useVercelChat.ts` around lines 325 - 332, The current token-injection
before calling sendMessage overwrites any existing headers on
chatRequestOptions; update the merge so headers = {
...(chatRequestOptions.headers || {}), Authorization: `Bearer ${freshToken}` }
when building requestOptions (inside the getAccessToken branch) so existing
headers are preserved—apply this change where getAccessToken,
chatRequestOptions, initialMessage and sendMessage are used.
Automated PR from coding agent.
Summary by CodeRabbit