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 attachRequestIdInterceptor and getRequestIdFromError; correctly uses ?? for the fallback chain and lowercased response-header key. |
| frontend/src/helpers/requestId.test.js | Good test coverage for both helpers; runRequestInterceptors accesses interceptors.request.handlers, an internal axios API that could silently break on a major axios upgrade. |
| frontend/src/App.jsx | Uses Symbol.for flag to prevent duplicate global-interceptor registration on HMR; renders copyable request-ID widget in error toasts and appends it to log messages. |
| frontend/src/hooks/useAxiosPrivate.js | Correctly registers the request-ID interceptor alongside the response interceptor and ejects both in the cleanup function. |
| frontend/src/hooks/useExceptionHandler.jsx | Refactored to a single inner alert factory that closes over requestId; every return path now carries the request ID without duplicating fields. |
| frontend/src/store/alert-store.js | Adds requestId: null to the default alert shape, keeping the store backward-compatible. |
| frontend/src/index.css | Adds BEM-scoped CSS for the new request-ID row; word-break: break-all on the UUID value is appropriate. |
Sequence Diagram
sequenceDiagram
participant Browser as Browser (App.jsx)
participant Interceptor as Request Interceptor (requestId.js)
participant Backend as Backend API
participant Handler as useExceptionHandler
participant Store as alert-store
participant Toast as Notification Toast
participant Logs as Logs Panel
Browser->>Interceptor: outgoing axios request
Interceptor->>Interceptor: X-Request-ID absent?
Interceptor-->>Browser: inject X-Request-ID: uuidv4()
Browser->>Backend: HTTP request with X-Request-ID
alt Success
Backend-->>Browser: 2xx response
else Error
Backend-->>Browser: error response with x-request-id
Browser->>Handler: handleException(err)
Handler->>Handler: getRequestIdFromError(err) response header ?? config header
Handler->>Store: type error with requestId
Store->>Toast: alertDetails with requestId
Toast->>Toast: render copyable UUID widget
Toast->>Logs: pushLogMessages with Request ID line
end
Reviews (7): 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
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.