UN-3444 [FEAT] Propagate frontend request ID and surface it on error notifications#1955
Conversation
…notifications - Add `X-Request-ID` request interceptor (uuidv4) on the global axios instance and on each `useAxiosPrivate` axios instance so every API call carries a client-generated correlation ID. Django/Flask backends already honor incoming `X-Request-ID`, so backend logs reuse the same value. - Extract the request ID in `useExceptionHandler` from response headers with a fallback to the outgoing request headers (covers network/cancel errors). - Thread `requestId` through the alert store and render it in the error notification with an antd `Typography.Text copyable` for a one-click copy.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds X-Request-ID support: header helpers and tests, module- and hook-level Axios interceptors, error extraction into exception handling, alert state updates, notification rendering with a copyable Request ID, and accompanying CSS. ChangesRequest ID Tracking
Sequence DiagramsequenceDiagram
participant App
participant Axios
participant RequestInterceptor as attachRequestIdInterceptor
participant Server
participant ExceptionHandler as useExceptionHandler
participant AlertStore as useAlertStore
participant Notification
App->>Axios: attachRequestIdInterceptor(axios)
App->>Axios: send HTTP request
Axios->>RequestInterceptor: outgoing request
RequestInterceptor->>RequestInterceptor: ensure/generate X-Request-ID
RequestInterceptor->>Server: request + X-Request-ID
Server-->>Axios: response (error) + X-Request-ID header
Axios->>ExceptionHandler: error thrown
ExceptionHandler->>ExceptionHandler: getRequestIdFromError(err)
ExceptionHandler->>AlertStore: setAlertDetails(..., requestId)
AlertStore->>Notification: open with description + requestId
Notification->>Notification: render content and Request ID block
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
|
| Filename | Overview |
|---|---|
| frontend/src/helpers/requestId.js | New helper with interceptor registration and request-ID extraction; handles both AxiosHeaders and plain-object headers, uses nullish-coalescing for fallback chain. |
| frontend/src/helpers/requestId.test.js | Good coverage of core paths, but all header-presence checks exercise the plain-object branch rather than the AxiosHeaders.set branch that runs in production. |
| frontend/src/App.jsx | Symbol.for flag prevents duplicate interceptor registration on HMR; adds request-ID line to error toasts and log messages correctly gated on type === "error". |
| frontend/src/hooks/useAxiosPrivate.js | Interceptor registered and ejected symmetrically in useEffect; correctly pairs with the existing response interceptor cleanup. |
| frontend/src/hooks/useExceptionHandler.jsx | requestId threaded through all return paths via local closure; optional chaining in getRequestIdFromError makes the null-err path safe. |
| frontend/src/store/alert-store.js | Additive null default for requestId; no behaviour change for existing non-error alerts. |
| frontend/src/index.css | BEM-styled rules for toast request-ID display; word-break and flex layout look correct. |
Sequence Diagram
sequenceDiagram
participant UI as React UI
participant Int as RequestId Interceptor
participant BE as Backend (Django/Flask)
participant Toast as Error Toast
participant Logs as Logs Panel
UI->>Int: axios request (no X-Request-ID)
Int->>Int: inject X-Request-ID: uuidv4()
Int->>BE: request with X-Request-ID header
BE-->>Int: response (echoes X-Request-ID in header)
Int-->>UI: error response
UI->>UI: getRequestIdFromError(err) → response header ?? config header
UI->>Toast: render Request ID with copyable widget
UI->>Logs: append Request ID to log row
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
frontend/src/helpers/requestId.test.js:33-44
**Test helper always exercises the plain-object branch**
`runRequestInterceptors` spreads `config.headers` into a plain `{}`, so `setHeaderIfMissing` always takes the `!headers.set` path in every test. The production code path — where `config.headers` is an `AxiosHeaders` instance and `headers.set(name, value, false)` is called — is never exercised. If the `AxiosHeaders.set(…, false)` rewrite guard ever behaves differently than expected (e.g. a future axios change), these tests would still pass without catching it. Consider adding a test that passes an `AxiosHeaders` instance directly to confirm the `rewrite: false` guard prevents overwrites.
Reviews (8): Last reviewed commit: "Merge branch 'main' into UN-3444-impleme..." | Re-trigger Greptile
- Use nullish coalescing in `getRequestIdFromError` so an empty-string backend-echoed header isn't silently shadowed by the request-side header. - Drop the redundant mixed-case response-header lookup; browser/axios response headers are always lower-cased. - Export the global axios interceptor ID so HMR-friendly cleanup is possible (and the chain doesn't leak across module re-evals).
…rom-frontend-to-backend
… panel Notification rows in the Logs & Notifications table now include the request ID on a second line (markdown inline-code) so users can reference it without opening the toast.
…rom-frontend-to-backend
vishnuszipstack
left a comment
There was a problem hiding this comment.
PR Review Toolkit — automated multi-agent review
Ran Comment Analyzer, PR Test Analyzer, Silent Failure Hunter, Type Design Analyzer, Code Reviewer, and Code Simplifier against git diff main...HEAD. Overall the feature is well-structured and the helpers are cleanly extracted as pure functions. Findings below are posted inline, prioritized:
P1 (address before merge)
requestId.js:7— interceptor uses bracket access onconfig.headers(anAxiosHeadersinstance in axios 1.x) and assumes it is defined.
P2 (recommended)
requestId.js:5— no tests added; the pure helpers are trivially testable (vitest is configured).requestId.js:16— response/config header-casing asymmetry is correct but fragile.App.jsx:17— module-level side effect at import time; interceptor never ejected; unused export.useExceptionHandler.jsx:9—buildAlertnow takes 4 positional args;requestIddefault (undefined) diverges fromalert-store.js(null);propTypesblock is stale.
P3 (nice-to-have)
index.css:264—font-size: 12pxduplicated across all three rules.App.jsx:62— inline description JSX could be extracted; redundant optional chaining inside theshowRequestIdguard.
Note: the Comment Analyzer found no comment-accuracy issues (the PR adds no code comments).
- Use AxiosHeaders.set(name, value, false) when available so the case-insensitive normalization is respected and caller-supplied IDs are preserved; fall back to bracket access for plain-object headers. - Guard against undefined config.headers on hand-built request configs. - Collapse buildAlert into a local alert(content) closure inside handleException so each return site stops repeating title/duration/ requestId. Default requestId to null to match the alert-store shape. - Drop the stale, function-shaped useExceptionHandler.propTypes block. - Make the global axios attach HMR-safe with a Symbol-based idempotency guard; remove the unused exported handle. - Consolidate the .notification-request-id font-size rules and remove the now-empty __label class. - Add vitest coverage for the helper (injection, no-overwrite, distinct IDs per request, fallback chain, null/empty inputs).
|
@Deepak-Kesavan approving. check the sonar issues. |
- Drop the useless `|| {}` fallback when spreading config.headers.
- Use optional chaining `handler?.fulfilled` instead of `handler && handler.fulfilled`.
…rom-frontend-to-backend
|
…rom-frontend-to-backend
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |



What
X-Request-ID: <uuidv4>on every outgoing API call (both on the globalaxiosdefault instance and on eachuseAxiosPrivateinstance).err.response.headers['x-request-id'](with a fallback toerr.config.headers['X-Request-ID']for network/cancel errors) and thread it throughuseExceptionHandlerand the alert store.Typography.Text copyable(one-click copy).Why
X-Request-ID, but the frontend was neither sending one nor surfacing it to users.How
frontend/src/helpers/requestId.jsexposesattachRequestIdInterceptor(instance)andgetRequestIdFromError(err). Interceptor only sets the header if one is not already on the request, so service-to-service hops (e.g.tool-sandboxalready sendsX-Request-ID) aren't overwritten.App.jsxattaches the interceptor to the globalaxiosimport at module load; its returned handle is exported asglobalRequestIdInterceptorso HMR-friendly cleanup is possible.useAxiosPrivate.jsregisters + ejects the interceptor alongside the existing response interceptor.useExceptionHandler.jsxaddsrequestIdto everybuildAlertreturn path (including the no-response branches).App.jsxrenders the request ID line in the notification description (copyable widget) and appendsRequest ID: \`to the log message thatpushLogMessageswrites — the table column already renders markdown viaCustomMarkdown`, which formats the UUID as an inline code pill.alert-store.jsextends the default alert shape withrequestId.index.cssadds BEM-styled.notification-request-idfor spacing/layout in the toast.Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
X-Request-ID(e.g.tool-sandbox→ runner) keeps its value.useExceptionHandlerretains every prior return path;requestIdis an additive optional field. The notification only renders the new line when an error alert has a request ID. The log message is augmented forNOTIFICATIONrows only —LOGrows from socket messages are unchanged.Database Migrations
Env Config
Relevant Docs
backend/middleware/request_id.py,backend/backend/settings/base.py—LOG_REQUEST_ID_HEADER,REQUEST_ID_RESPONSE_HEADER,GENERATE_REQUEST_ID_IF_NOT_IN_HEADER).unstract/core/src/unstract/core/flask/middleware.py:assign_request_id.Related Issues or PRs
Dependencies Versions
uuiddep (already infrontend/package.json). No new dependencies.Notes on Testing
Request ID: <uuid>with a copy icon that copies the raw UUID.X-Request-IDand the value matches what is shown in the UI.x-request-idfrom the response requiresCORS_EXPOSE_HEADERS = ["X-Request-ID"]on the backend; the fallback toerr.config.headerskeeps the feature working regardless because the backend reuses the header we sent.Screenshots
Checklist
I have read and understood the Contribution Guidelines.