Repository navigation
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: sentry 설정
refactor : support 페이지 보충
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR integrates Sentry observability across the Next.js application with server, edge, and client-side instrumentation, error capture, and user context tracking. It adds a news upload section to the support page and removes an unused reporting hook. ChangesSentry Monitoring & UI Updates
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
There was a problem hiding this comment.
Code Review
This pull request integrates Sentry for error tracking and performance monitoring in a Next.js application, configures user context tracking, and cleans up several unused API hooks and services. The review feedback highlights a critical issue in the API client where parsing custom error codes (e.g., COMMON500) fails to report 5xx errors to Sentry. Additionally, the reviewer recommends dynamically configuring Sentry's tracesSampleRate based on the environment to protect quotas, correcting a potential typo in the support page link, and using a unique database ID instead of an email address for Sentry user identification to safeguard PII.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (error instanceof ApiError) { | ||
| // 5xx 서버 에러만 Sentry에 보고 (4xx, 401은 제외) | ||
| const statusCode = parseInt(error.code.replace("HTTP", ""), 10); | ||
| if (!isNaN(statusCode) && statusCode >= 500) { | ||
| Sentry.captureException(error, { | ||
| extra: { code: error.code, url: requestUrl, method: options.method }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
error.code가 COMMON500이나 BLOCK_400과 같이 숫자가 포함된 커스텀 에러 코드일 경우, error.code.replace("HTTP", "") 결과는 여전히 숫자가 아닌 문자로 시작하는 문자열(예: COMMON500)이 됩니다. 이로 인해 parseInt 결과가 NaN이 되어 5xx 서버 에러임에도 불구하고 Sentry에 보고되지 않는 문제가 발생할 수 있습니다.\n\n정규식을 사용하여 에러 코드 문자열에서 숫자 부분만 안전하게 추출하여 판별하는 것을 권장합니다.
if (error instanceof ApiError) {
// 5xx 서버 에러만 Sentry에 보고 (4xx, 401은 제외)
const match = error.code.match(/\d+/);
const statusCode = match ? parseInt(match[0], 10) : NaN;
if (!isNaN(statusCode) && statusCode >= 500) {
Sentry.captureException(error, {
extra: { code: error.code, url: requestUrl, method: options.method },
});
}
}| // 성능 모니터링: 페이지 로딩, API 응답 시간 등 100% 수집 | ||
| // production 배포 시 0.1~0.2 (10~20%)로 낮추는 것을 권장 | ||
| tracesSampleRate: 1.0, |
There was a problem hiding this comment.
현재 tracesSampleRate가 1.0으로 고정되어 있습니다. 주석에 언급된 것처럼 운영(production) 환경에서는 1020%(0.10.2)로 낮추는 것이 권장되는데, 이를 수동으로 변경하기보다 환경 변수(process.env.NODE_ENV)에 따라 자동으로 설정되도록 분기 처리하는 것이 안전합니다.
| // 성능 모니터링: 페이지 로딩, API 응답 시간 등 100% 수집 | |
| // production 배포 시 0.1~0.2 (10~20%)로 낮추는 것을 권장 | |
| tracesSampleRate: 1.0, | |
| // 성능 모니터링: 페이지 로딩, API 응답 시간 등 수집 | |
| // production 배포 시 0.1~0.2 (10~20%)로 낮추는 것을 권장 | |
| tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0, |
| environment: process.env.NODE_ENV, | ||
| tracesSampleRate: 1.0, |
There was a problem hiding this comment.
| environment: process.env.NODE_ENV, | ||
| tracesSampleRate: 1.0, |
There was a problem hiding this comment.
| <a | ||
| href={EXTERNAL_LINKS.NEWS_FROM_URL} | ||
| target="_blank" |
There was a problem hiding this comment.
EXTERNAL_LINKS.NEWS_FROM_URL 변수명에 오타가 있는 것으로 보입니다. "소식 업로드"를 위한 설문지(Form) 링크이므로 NEWS_FROM_URL 대신 NEWS_FORM_URL이 더 적절합니다.\n\n정의된 상수 파일(EXTERNAL_LINKS가 선언된 곳)과 함께 이 부분의 변수명을 NEWS_FORM_URL로 변경하는 것을 검토해 주세요.
| <a | |
| href={EXTERNAL_LINKS.NEWS_FROM_URL} | |
| target="_blank" | |
| <a | |
| href={EXTERNAL_LINKS.NEWS_FORM_URL} | |
| target="_blank" |
| Sentry.setUser({ | ||
| id: response.result.email || "unknown", | ||
| email: response.result.email || undefined, | ||
| username: response.result.nickname, | ||
| }); |
There was a problem hiding this comment.
Sentry의 setUser에서 id 필드에 이메일(email)을 사용하고 있습니다. 이메일은 변경될 수 있는 값이며 개인정보(PII)에 해당하므로, Sentry의 권장사항에 따라 데이터베이스의 고유 식별자(예: id 또는 userId)를 id 필드에 사용하고 이메일은 email 필드에만 할당하는 것이 보안 및 데이터 정합성 측면에서 더 좋습니다.\n\nresponse.result에 고유 ID 필드가 존재한다면 이를 활용해 주세요.
| Sentry.setUser({ | |
| id: response.result.email || "unknown", | |
| email: response.result.email || undefined, | |
| username: response.result.nickname, | |
| }); | |
| Sentry.setUser({ | |
| id: String(response.result.id || "unknown"), | |
| email: response.result.email || undefined, | |
| username: response.result.nickname, | |
| }); |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
src/components/auth/AuthProvider.tsx (1)
21-25: ⚡ Quick winConsider using non-PII identifier as Sentry user ID.
The implementation uses
id(line 22). Email is personally identifiable information (PII). Sentry's best practice recommends using a non-PII identifier (such as a user UUID or database ID) as the primaryid, while keepingThis reduces privacy/compliance risk and makes it easier to anonymize or delete user data when needed.
🔒 Proposed fix if user profile includes an ID field
Sentry.setUser({ - id: response.result.email || "unknown", + id: response.result.id?.toString() || "unknown", email: response.result.email || undefined, username: response.result.nickname, });If the profile doesn't include a unique ID, consider adding one or using a hashed/anonymized version of the email.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/auth/AuthProvider.tsx` around lines 21 - 25, The Sentry.setUser call is currently using PII (response.result.email) as the user id; update the Sentry.setUser invocation in AuthProvider (the Sentry.setUser call) to use a non-PII identifier such as response.result.id or response.result.userId if available, falling back to an anonymized/hash of response.result.email only if no unique id exists, and keep response.result.email assigned to the email field (not the id) while preserving username: response.result.nickname so Sentry receives id (non-PII), email (PII) separately, and username unchanged.instrumentation.ts (1)
4-9: ⚡ Quick winUse
else iffor mutually exclusive runtime checks.The sequential
ifstatements could theoretically load both configs ifNEXT_RUNTIMEcontained both values. While unlikely, usingelse ifmakes the mutual exclusion explicit and prevents potential double-initialization.♻️ Proposed fix
export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { await import("./sentry.server.config"); - } - if (process.env.NEXT_RUNTIME === "edge") { + } else if (process.env.NEXT_RUNTIME === "edge") { await import("./sentry.edge.config"); } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@instrumentation.ts` around lines 4 - 9, The two runtime checks using process.env.NEXT_RUNTIME should be mutually exclusive; replace the second standalone if with an else if so only one config is imported: change the block that currently awaits import("./sentry.server.config") and import("./sentry.edge.config") to use if (process.env.NEXT_RUNTIME === "nodejs") { await import("./sentry.server.config"); } else if (process.env.NEXT_RUNTIME === "edge") { await import("./sentry.edge.config"); } to prevent potential double-initialization.sentry.edge.config.ts (1)
4-4: ⚖️ Poor tradeoffHardcoded Sentry DSN spans three configuration files.
The same Sentry DSN is hardcoded in
sentry.edge.config.ts,sentry.server.config.ts, andinstrumentation-client.ts. While Sentry DSNs are technically public (used client-side), hardcoding prevents:
- Environment-specific projects (dev/staging/production)
- Easy rotation if needed
- Centralized configuration management
Move to a shared environment variable (
NEXT_PUBLIC_SENTRY_DSN) that all three configs can reference.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sentry.edge.config.ts` at line 4, Replace the hardcoded DSN value assigned to the dsn property in the Sentry configuration with a shared environment variable NEXT_PUBLIC_SENTRY_DSN; update the dsn assignment (the dsn field in sentry.edge.config.ts and the equivalent dsn usages in sentry.server.config.ts and instrumentation-client.ts) to read from process.env.NEXT_PUBLIC_SENTRY_DSN (or the appropriate runtime env accessor) and add a clear fallback/error handling if the variable is missing so builds fail fast or log a helpful message.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@instrumentation-client.ts`:
- Around line 7-9: The tracesSampleRate is hardcoded to 1.0; change it to read
from an environment-driven value and set a lower default for production (e.g.,
use process.env.SENTRY_TRACES_SAMPLE_RATE or compute based on NODE_ENV so
production uses 0.1–0.2 while development can remain 1.0). Update the
tracesSampleRate assignment in instrumentation-client.ts (the variable named
tracesSampleRate) to parse the env value (fallback to 0.1–0.2 in production and
1.0 in non-production) so production sampling is reduced without altering
behavior in dev.
In `@sentry.edge.config.ts`:
- Line 6: All configs currently hardcode tracesSampleRate: 1.0; change them to
compute the sampling from environment (e.g. read
process.env.SENTRY_TRACES_SAMPLE_RATE or NODE_ENV) by adding a small helper
(name it getTracesSampleRate or computeTracesSampleRate) that returns a float:
if SENTRY_TRACES_SAMPLE_RATE is set use parseFloat(value), else if NODE_ENV ===
'production' return 0.1 (or 0.2) otherwise return 1.0 for dev; then replace
tracesSampleRate: 1.0 in sentry.edge.config.ts, sentry.server.config.ts and
instrumentation-client.ts with tracesSampleRate: getTracesSampleRate() so
production uses the lower default and you can override via env.
In `@src/app/global-error.tsx`:
- Around line 17-26: The <html> element returned by the global error component
is missing a lang attribute; update the returned <html> tag in the component
(the JSX that currently returns <html> ... ) to include an appropriate lang
value (e.g., lang="ko" for the Korean message) so screen readers and tools can
detect the page language.
In `@src/lib/api/client.ts`:
- Around line 91-98: The current Sentry-reporting in the client.ts ApiError
branch assumes error.code is prefixed with "HTTP" and thus misses custom backend
codes; update the ApiError class (ApiError constructor / properties) to include
a numeric status field (e.g., status?: number) and ensure the code that
constructs ApiError in the HTTP response path sets that status from
response.status; then change the Sentry reporting logic in client.ts (the block
that computes statusCode from error.code and calls Sentry.captureException) to
prefer error.status if present, otherwise only parse error.code when it
startsWith("HTTP"), and report to Sentry for status >= 500 using the resolved
numeric status.
---
Nitpick comments:
In `@instrumentation.ts`:
- Around line 4-9: The two runtime checks using process.env.NEXT_RUNTIME should
be mutually exclusive; replace the second standalone if with an else if so only
one config is imported: change the block that currently awaits
import("./sentry.server.config") and import("./sentry.edge.config") to use if
(process.env.NEXT_RUNTIME === "nodejs") { await
import("./sentry.server.config"); } else if (process.env.NEXT_RUNTIME ===
"edge") { await import("./sentry.edge.config"); } to prevent potential
double-initialization.
In `@sentry.edge.config.ts`:
- Line 4: Replace the hardcoded DSN value assigned to the dsn property in the
Sentry configuration with a shared environment variable NEXT_PUBLIC_SENTRY_DSN;
update the dsn assignment (the dsn field in sentry.edge.config.ts and the
equivalent dsn usages in sentry.server.config.ts and instrumentation-client.ts)
to read from process.env.NEXT_PUBLIC_SENTRY_DSN (or the appropriate runtime env
accessor) and add a clear fallback/error handling if the variable is missing so
builds fail fast or log a helpful message.
In `@src/components/auth/AuthProvider.tsx`:
- Around line 21-25: The Sentry.setUser call is currently using PII
(response.result.email) as the user id; update the Sentry.setUser invocation in
AuthProvider (the Sentry.setUser call) to use a non-PII identifier such as
response.result.id or response.result.userId if available, falling back to an
anonymized/hash of response.result.email only if no unique id exists, and keep
response.result.email assigned to the email field (not the id) while preserving
username: response.result.nickname so Sentry receives id (non-PII), email (PII)
separately, and username unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 481b6f60-bda2-43e4-8db3-7b5e16bb9d99
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
instrumentation-client.tsinstrumentation.tsnext.config.tspackage.jsonsentry.edge.config.tssentry.server.config.tssrc/app/global-error.tsxsrc/app/support/page.tsxsrc/components/auth/AuthProvider.tsxsrc/hooks/mutations/useReportMemberMutations 2.tssrc/hooks/queries/useMeetingChatQueries 2.tssrc/hooks/queries/useMeetingTopicQueries 2.tssrc/hooks/useReportBlockFlow 2.tssrc/lib/api/ApiError.tssrc/lib/api/client.tssrc/lib/api/client/index.tssrc/lib/api/errorMapper.tssrc/services/meetingChatService 2.tssrc/services/reportService 2.ts
💤 Files with no reviewable changes (9)
- src/services/reportService 2.ts
- src/hooks/queries/useMeetingChatQueries 2.ts
- src/lib/api/ApiError.ts
- src/services/meetingChatService 2.ts
- src/hooks/queries/useMeetingTopicQueries 2.ts
- src/lib/api/client/index.ts
- src/hooks/mutations/useReportMemberMutations 2.ts
- src/lib/api/errorMapper.ts
- src/hooks/useReportBlockFlow 2.ts
| // 성능 모니터링: 페이지 로딩, API 응답 시간 등 100% 수집 | ||
| // production 배포 시 0.1~0.2 (10~20%)로 낮추는 것을 권장 | ||
| tracesSampleRate: 1.0, |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Implement the acknowledged sampling rate adjustment.
The comment correctly notes that tracesSampleRate: 1.0 should be lowered to 0.1-0.2 for production, but the code doesn't implement this. This can lead to high Sentry costs and rate-limit issues in production.
⚡ Proposed fix
// 성능 모니터링: 페이지 로딩, API 응답 시간 등 100% 수집
- // production 배포 시 0.1~0.2 (10~20%)로 낮추는 것을 권장
- tracesSampleRate: 1.0,
+ // production 배포 시 0.1~0.2 (10~20%)로 낮춤
+ tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0,📝 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.
| // 성능 모니터링: 페이지 로딩, API 응답 시간 등 100% 수집 | |
| // production 배포 시 0.1~0.2 (10~20%)로 낮추는 것을 권장 | |
| tracesSampleRate: 1.0, | |
| // 성능 모니터링: 페이지 로딩, API 응답 시간 등 100% 수집 | |
| // production 배포 시 0.1~0.2 (10~20%)로 낮춤 | |
| tracesSampleRate: process.env.NODE_ENV === "production" ? 0.1 : 1.0, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@instrumentation-client.ts` around lines 7 - 9, The tracesSampleRate is
hardcoded to 1.0; change it to read from an environment-driven value and set a
lower default for production (e.g., use process.env.SENTRY_TRACES_SAMPLE_RATE or
compute based on NODE_ENV so production uses 0.1–0.2 while development can
remain 1.0). Update the tracesSampleRate assignment in instrumentation-client.ts
(the variable named tracesSampleRate) to parse the env value (fallback to
0.1–0.2 in production and 1.0 in non-production) so production sampling is
reduced without altering behavior in dev.
| Sentry.init({ | ||
| dsn: "https://415293b2a74c82af51a4d9b6f9436fbb@o4511437350436864.ingest.us.sentry.io/4511518360141824", | ||
| environment: process.env.NODE_ENV, | ||
| tracesSampleRate: 1.0, |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
100% trace sampling rate across all Sentry configs.
All three Sentry configurations (sentry.edge.config.ts, sentry.server.config.ts, instrumentation-client.ts) use tracesSampleRate: 1.0. In production, this captures 100% of all performance traces, which can:
- Generate high Sentry costs
- Hit rate limits
- Create unnecessary noise
The comment in instrumentation-client.ts acknowledges this should be 0.1-0.2 for production. Implement environment-based sampling across all configs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sentry.edge.config.ts` at line 6, All configs currently hardcode
tracesSampleRate: 1.0; change them to compute the sampling from environment
(e.g. read process.env.SENTRY_TRACES_SAMPLE_RATE or NODE_ENV) by adding a small
helper (name it getTracesSampleRate or computeTracesSampleRate) that returns a
float: if SENTRY_TRACES_SAMPLE_RATE is set use parseFloat(value), else if
NODE_ENV === 'production' return 0.1 (or 0.2) otherwise return 1.0 for dev; then
replace tracesSampleRate: 1.0 in sentry.edge.config.ts, sentry.server.config.ts
and instrumentation-client.ts with tracesSampleRate: getTracesSampleRate() so
production uses the lower default and you can override via env.
| return ( | ||
| <html> | ||
| <body> | ||
| <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: "100vh", gap: "16px" }}> | ||
| <h2>예기치 못한 문제가 발생했습니다.</h2> | ||
| <button onClick={() => reset()}>다시 시도하기</button> | ||
| </div> | ||
| </body> | ||
| </html> | ||
| ); |
There was a problem hiding this comment.
Add lang attribute to <html> tag for accessibility.
The <html> tag is missing the lang attribute (line 18). This attribute helps screen readers, translation tools, and search engines understand the page language.
♿ Proposed fix
return (
- <html>
+ <html lang="ko">
<body>
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: "100vh", gap: "16px" }}>📝 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.
| return ( | |
| <html> | |
| <body> | |
| <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: "100vh", gap: "16px" }}> | |
| <h2>예기치 못한 문제가 발생했습니다.</h2> | |
| <button onClick={() => reset()}>다시 시도하기</button> | |
| </div> | |
| </body> | |
| </html> | |
| ); | |
| return ( | |
| <html lang="ko"> | |
| <body> | |
| <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: "100vh", gap: "16px" }}> | |
| <h2>예기치 못한 문제가 발생했습니다.</h2> | |
| <button onClick={() => reset()}>다시 시도하기</button> | |
| </div> | |
| </body> | |
| </html> | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/global-error.tsx` around lines 17 - 26, The <html> element returned
by the global error component is missing a lang attribute; update the returned
<html> tag in the component (the JSX that currently returns <html> ... ) to
include an appropriate lang value (e.g., lang="ko" for the Korean message) so
screen readers and tools can detect the page language.
| if (error instanceof ApiError) { | ||
| // 5xx 서버 에러만 Sentry에 보고 (4xx, 401은 제외) | ||
| const statusCode = parseInt(error.code.replace("HTTP", ""), 10); | ||
| if (!isNaN(statusCode) && statusCode >= 500) { | ||
| Sentry.captureException(error, { | ||
| extra: { code: error.code, url: requestUrl, method: options.method }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Error code parsing may miss custom 5xx error codes.
Line 93 assumes error.code has an "HTTP" prefix, but looking at line 77, error.code can be either from data?.code (custom error codes) or HTTP${response.status}. If the backend returns a custom error code (e.g., "SERVER_ERROR") for a 5xx response, parsing will produce NaN and the error won't be reported to Sentry, even though it's a server error.
Consider either:
- Storing the original HTTP status separately in ApiError
- Attempting to parse but falling back to reporting all ApiErrors
- Checking if error.code starts with "HTTP" before parsing
🐛 Proposed fix option 1: Store HTTP status in ApiError
Modify ApiError class to include HTTP status:
// In src/lib/api/errors/ApiError.ts
export class ApiError extends Error {
code: string;
response?: any;
status?: number; // Add this
constructor(message: string, code: string = "UNKNOWN_ERROR", response?: any, status?: number) {
super(message);
this.name = "ApiError";
this.code = code;
this.response = response;
this.status = status; // Store it
}
}Then in client.ts line 83:
- throw new ApiError(errorMessage, errorCode, data);
+ throw new ApiError(errorMessage, errorCode, data, response.status);And update the Sentry reporting:
if (error instanceof ApiError) {
- // 5xx 서버 에러만 Sentry에 보고 (4xx, 401은 제외)
- const statusCode = parseInt(error.code.replace("HTTP", ""), 10);
- if (!isNaN(statusCode) && statusCode >= 500) {
+ if (error.status && error.status >= 500) {
Sentry.captureException(error, {
- extra: { code: error.code, url: requestUrl, method: options.method },
+ extra: { code: error.code, status: error.status, url: requestUrl, method: options.method },
});
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/api/client.ts` around lines 91 - 98, The current Sentry-reporting in
the client.ts ApiError branch assumes error.code is prefixed with "HTTP" and
thus misses custom backend codes; update the ApiError class (ApiError
constructor / properties) to include a numeric status field (e.g., status?:
number) and ensure the code that constructs ApiError in the HTTP response path
sets that status from response.status; then change the Sentry reporting logic in
client.ts (the block that computes statusCode from error.code and calls
Sentry.captureException) to prefer error.status if present, otherwise only parse
error.code when it startsWith("HTTP"), and report to Sentry for status >= 500
using the resolved numeric status.
Summary by CodeRabbit