{"record":{"id":"69bad68006347ca0","repo":"pinpoint-apm/pinpoint","slug":"an-error-occurred-while-fetching-the-data","errorCode":null,"errorMessage":"An error occurred while fetching the data.","messagePattern":"An error occurred while fetching the data\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web-frontend/src/main/v3/packages/ui/src/hooks/api/reactQueryHelper.tsx","lineNumber":65,"sourceCode":"  }\n\n  if (isServerErrorResponse(body)) {\n    const serverError = body;\n    const err = new Error(\n      serverError.detail || serverError.title || 'An error occurred while fetching the data.',\n    ) as Error & ErrorResponse;\n    Object.assign(err, serverError);\n    err.message = serverError.detail || serverError.title || err.message;\n    throw err;\n  }\n\n  const detail =\n    typeof (body as Record<string, unknown>)?.detail === 'string'\n      ? (body as Record<string, unknown>).detail\n      : typeof (body as Record<string, unknown>)?.message === 'string'\n        ? (body as Record<string, unknown>).message\n        : undefined;\n  throw new Error((detail as string) || 'An error occurred while fetching the data.');\n}\n\nexport interface QueryFnOptions {\n  /**\n   * 이 요청이 조회할 service. 지정하면 `pServiceName` 헤더를 여기서 직접 싣고,\n   * fetch 인터셉터는 이미 실린 헤더를 덮어쓰지 않는다.\n   *\n   * 화면의 service와 조회 대상의 service가 다를 때 쓴다(`useServerMapTargetServiceName`).\n   * 인터셉터는 경로/전역 선택값만 보므로 \"고른 노드가 다른 service 소속\"이라는 사실을 알 수 없다.\n   *\n   * 이 값을 싣는 훅은 queryKey에도 같은 값을 넣어야 한다. 헤더만 갈리고 캐시 키가 같으면\n   * 이름이 같은 다른 service의 application끼리 캐시가 섞인다.\n   */\n  serviceName?: string;\n}\n\nexport const queryFn = (url: string, options?: QueryFnOptions) => async () => {\n  const response = options?.serviceName","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/web-frontend/src/main/v3/packages/ui/src/hooks/api/reactQueryHelper.tsx#L47-L83","documentation":"When parseResponseError successfully parses the response body as JSON but the body is not a recognized server-error shape (and no 'detail' or 'message' string field exists), the helper throws the fallback Error 'An error occurred while fetching the data.' It is a last-resort message meaning the request failed with a non-OK status but carried no usable error detail.","triggerScenarios":"A non-2xx HTTP response whose JSON body has neither a string 'detail' nor 'message' field (nor matches isServerErrorResponse), e.g. {\"status\":500} or an empty JSON object.","commonSituations":"A new backend endpoint or middleware returns a JSON error shape the frontend helper does not understand; API gateways return minimal JSON like {\"error\":true}; API version drift between Pinpoint server and web frontend changes the error payload contract.","solutions":["Inspect the actual response in the network tab to find the real error behind the generic message (check status code and body).","Align the backend error payload so it includes a 'detail' or 'message' string field that parseResponseError can surface.","If the shape is a standard server error, verify isServerErrorResponse matches it; update the helper's parser if the contract changed.","Client-side: catch the error and add request context (URL, status) to logs since this message alone is uninformative."],"exampleFix":"// before (server)\nreturn ResponseEntity.status(500).body(Map.of(\"code\", 500));\n// after\nreturn ResponseEntity.status(500).body(Map.of(\"message\", \"agent delete failed: \" + reason));","handlingStrategy":"try-catch","validationCode":"const res = await fetch(url);\nif (!res.ok) {\n  const body = await res.clone().json().catch(() => null);\n  if (body && typeof body.detail !== 'string' && typeof body.message !== 'string') {\n    console.warn('Server returned unrecognizable error shape', res.status, body);\n  }\n}","typeGuard":"const hasErrorDetail = (body: unknown): body is { detail: string } | { message: string } =>\n  typeof body === 'object' && body !== null &&\n  (typeof (body as any).detail === 'string' || typeof (body as any).message === 'string');","tryCatchPattern":"try {\n  await mutation.mutateAsync(payload);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg === 'An error occurred while fetching the data.') {\n    logWithContext('Uninformative API error — check network tab for status/body', { url: requestUrl });\n    showError('Request failed; see logs for details.');\n  } else showError(msg);\n}","preventionTips":["Standardize backend error payloads to always include a string 'detail' or 'message' field.","Keep frontend error-body parsers (isServerErrorResponse / detail-message extraction) in sync with server API versions.","Always log the HTTP status and URL alongside this generic error message.","Add integration tests asserting that real backend error responses surface meaningful messages in the UI.","Wrap mutations with react-query onError handlers that enrich the generic message with context."],"tags":["http","fetch","react-query","error-handling","frontend"],"backgroundTag":"http-error-response","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}