-
Notifications
You must be signed in to change notification settings - Fork 403
Open
Description
Bug Description
The useStream hook in @langchain/langgraph-sdk/react uses a stale client reference after the apiKey prop changes. This causes HTTP 403 authentication errors when the JWT token is refreshed while the component is mounted.
Root Cause
In useStream.ts, the useThreadHistory callback captures the initial client object in a closure:
// From node_modules/@langchain/langgraph-sdk/dist/react/useStream.js
const useThreadHistory = useCallback(
async (threadId, options) => {
// Uses `client` from closure - never updates when apiKey changes
const history = await client.threads.getHistory(threadId, options);
// ...
},
[] // Empty dependency array - closure is never refreshed
);The client is created from apiKey:
const client = useMemo(() => {
if (!apiUrl) return null;
return new Client({ apiUrl, apiKey });
}, [apiUrl, apiKey]);While client updates when apiKey changes, useThreadHistory still holds the old client reference due to the empty dependency array.
Reproduction Steps
- Mount a component using
useStreamwith an initial JWT token - Token expires/refreshes, parent component passes new
apiKeyprop - User sends a message via
submit() - Expected: Request uses new token
- Actual: Request uses old token → 403 error
Minimal Reproduction
function ChatComponent({ token }) {
const stream = useStream({
apiUrl: "https://api.example.com",
apiKey: token, // Changes when refreshed
assistantId: "my-assistant",
threadId: null,
});
// After token refresh, submit() still uses old token
return <button onClick={() => stream.submit(...)}>Send</button>;
}Current Workaround
Force component remount when token changes using React key:
<ChatComponent key={token || "no-token"} token={token} />This destroys and recreates the entire component, creating a new client instance.
Proposed Fix
Add client to the dependency array of useThreadHistory and other callbacks:
const useThreadHistory = useCallback(
async (threadId, options) => {
const history = await client.threads.getHistory(threadId, options);
// ...
},
[client] // Add client dependency
);Environment
@langchain/langgraph-sdk: ^0.0.41- React: 19.x
- Next.js: 15.x
Related Issues
- #4825 - Stale History in useStream
useStreamHook Not Updating Messages During Streaming with Proxy #1295 - useStream Hook Not Updating
Impact
- Prevents seamless token refresh in production apps
- Forces unnecessary component remounts as workaround
- Affects any app using JWT authentication with token rotation
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels