Skip to content

Add token renewal to agent chat transport#15727

Merged
FelixMalfait merged 1 commit intomainfrom
add-token-renew-to-agent-chat
Nov 9, 2025
Merged

Add token renewal to agent chat transport#15727
FelixMalfait merged 1 commit intomainfrom
add-token-renew-to-agent-chat

Conversation

@abdulrahmancodes
Copy link
Copy Markdown
Contributor

No description provided.

- Added `useSetRecoilState` for managing token state.
- Implemented `retryFetchWithRenewedToken` function to handle token renewal and retry fetch requests.
- Updated chat transport to utilize the new token renewal logic when a 401 status is encountered.
Comment on lines +87 to +97
updatedHeaders.set('Authorization', `Bearer ${renewedAccessToken}`);

return fetch(input, {
...init,
headers: updatedHeaders,
});
} catch {
setTokenPair(null);
return null;
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: retryFetchWithRenewedToken lacks synchronization, causing concurrent renewToken() calls and redundant server requests during multiple 401s.
Severity: HIGH | Confidence: 1.00

🔍 Detailed Analysis

When multiple HTTP requests simultaneously receive a 401 response, the retryFetchWithRenewedToken function in useAgentChat.ts allows each request to independently call renewToken(). This lack of synchronization leads to multiple concurrent token renewal requests being sent to the server, resulting in redundant API calls and concurrent updates to tokenPair and cookieStorage instead of a single, serialized renewal process.

💡 Suggested Fix

Implement a synchronization mechanism within retryFetchWithRenewedToken, similar to the fromPromise pattern used in apollo.factory.ts, to ensure only one renewToken() call executes at a time.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts#L54-L97

Potential issue: When multiple HTTP requests simultaneously receive a 401 response, the
`retryFetchWithRenewedToken` function in `useAgentChat.ts` allows each request to
independently call `renewToken()`. This lack of synchronization leads to multiple
concurrent token renewal requests being sent to the server, resulting in redundant API
calls and concurrent updates to `tokenPair` and `cookieStorage` instead of a single,
serialized renewal process.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR adds automatic token renewal to the agent chat transport layer to handle 401 authentication errors gracefully.

Key Changes:

  • Implemented custom fetch wrapper in DefaultChatTransport that intercepts 401 responses
  • Added retryFetchWithRenewedToken function to handle token renewal and retry logic
  • Imported necessary dependencies: renewToken from AuthService, tokenPairState, and cookie storage utilities
  • Follows the same token renewal pattern used in apollo.factory.ts for consistency

Implementation Details:
The solution intercepts HTTP responses at the transport level. When a 401 is detected, it calls the existing renewToken service, updates both Recoil state and cookie storage with the new tokens, then retries the original request with the refreshed authentication token. If renewal fails, it clears the token state and returns the original 401 response.

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended
  • The implementation follows existing patterns from apollo.factory.ts and correctly handles the token renewal flow. The logic is sound - it intercepts 401s, renews tokens, updates state, and retries. However, there are two minor issues: (1) the empty catch block silently swallows errors which could make debugging harder, and (2) one comment I left about infinite loops was incorrect upon deeper analysis - the fetch in retryFetchWithRenewedToken uses the global fetch, not the wrapped one, so no recursion occurs. The core functionality is solid.
  • No files require special attention - the implementation is straightforward and follows established patterns

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts 4/5 Added token renewal mechanism for 401 responses in agent chat transport with proper error handling

Sequence Diagram

sequenceDiagram
    participant User
    participant AgentChat as useAgentChat Hook
    participant Transport as DefaultChatTransport
    participant API as Agent Chat API
    participant Auth as renewToken Service
    participant Storage as Cookie Storage
    
    User->>AgentChat: Send message
    AgentChat->>Transport: sendMessage()
    Transport->>Transport: Custom fetch wrapper
    Transport->>API: POST agent chat stream endpoint
    
    alt Valid Token
        API-->>Transport: HTTP 200 Stream response
        Transport-->>AgentChat: messages updated
        AgentChat-->>User: Display response
    else Expired Token
        API-->>Transport: HTTP 401 Response
        Transport->>Transport: retryFetchWithRenewedToken()
        Transport->>Auth: Call renewToken with current tokenPair
        Auth->>API: GraphQL mutation to renew tokens
        
        alt Token Renewal Success
            API-->>Auth: New access and refresh tokens
            Auth-->>Transport: Returns renewed tokens
            Transport->>Storage: Save tokens to cookies
            Transport->>AgentChat: Update token state
            Transport->>API: Retry POST with refreshed token
            API-->>Transport: HTTP 200 Stream response
            Transport-->>AgentChat: messages updated
            AgentChat-->>User: Display response
        else Token Renewal Failure
            API-->>Auth: Error or invalid response
            Auth-->>Transport: Returns null or throws error
            Transport->>Storage: Clear stored tokens
            Transport->>AgentChat: Set token state to null
            Transport-->>AgentChat: Return original 401 response
            AgentChat-->>User: Show error state
        end
    end
Loading

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

...init,
headers: updatedHeaders,
});
} catch {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: empty catch block silently ignores all errors during token renewal, including network failures and unexpected errors

Suggested change
} catch {
} catch (error) {
if (isDebugMode) {
logDebug(`Token renewal failed: ${error}`);
}
setTokenPair(null);
return null;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts
Line: 93:93

Comment:
**style:** empty catch block silently ignores all errors during token renewal, including network failures and unexpected errors

```suggestion
    } catch (error) {
      if (isDebugMode) {
        logDebug(`Token renewal failed: ${error}`);
      }
      setTokenPair(null);
      return null;
    }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +105 to +115
fetch: async (input, init) => {
const response = await fetch(input, init);

if (response.status !== 401) {
return response;
}

const retriedResponse = await retryFetchWithRenewedToken(input, init);

return retriedResponse ?? response;
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: potential infinite loop - if token renewal returns 401 (e.g. invalid refresh token), this will retry indefinitely without preventing another 401 response

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts
Line: 105:115

Comment:
**logic:** potential infinite loop - if token renewal returns 401 (e.g. invalid refresh token), this will retry indefinitely without preventing another 401 response

How can I resolve this? If you propose a fix, please make it concise.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 9, 2025

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:52108

This environment will automatically shut down when the PR is closed or after 5 hours.

@FelixMalfait FelixMalfait merged commit 4a15cbc into main Nov 9, 2025
60 checks passed
@FelixMalfait FelixMalfait deleted the add-token-renew-to-agent-chat branch November 9, 2025 12:57
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 9, 2025

Thanks @abdulrahmancodes for your contribution!
This marks your 74th PR on the repo. You're top 1% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

NotYen pushed a commit to NotYen/twenty-ym that referenced this pull request Nov 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants