{"record":{"id":"bf831dff1cebf16e","repo":"pinpoint-apm/pinpoint","slug":"transaction-list-linked-transaction-not-found","errorCode":null,"errorMessage":"TRANSACTION_LIST.LINKED_TRANSACTION_NOT_FOUND","messagePattern":"TRANSACTION_LIST\\.LINKED_TRANSACTION_NOT_FOUND","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"web-frontend/src/main/v3/packages/ui/src/components/Transaction/call-tree/callTreeTableColumns.tsx","lineNumber":780,"sourceCode":"  // 않고, 열린 화면이 전역 선택값으로 조회한다(`resolveRequestService`).\n  const { serviceName } = useTransactionSearchParameters();\n\n  const handleClick = async (e: React.MouseEvent) => {\n    e.stopPropagation();\n    // fetch errors already surface via the global query error toast\n    const data = await queryClient\n      .fetchQuery(getTransactionTraceMetadataQueryOptions(traceId))\n      .catch(() => null);\n    if (data === null) {\n      return;\n    }\n    const rows = data?.metadata ?? [];\n\n    // A trace may contain multiple spans (multi-application); prefer the span the\n    // link points to, falling back to the first row (e.g. link target is a sub-span).\n    const preferred = rows.find((row) => String(row.spanId) === spanId) || rows[0];\n    if (!preferred?.applicationName || !preferred?.serviceType) {\n      toast.warn(t('TRANSACTION_LIST.LINKED_TRANSACTION_NOT_FOUND'));\n      return;\n    }\n\n    const baseTime = preferred.collectorAcceptTime;\n    const from = formatInTimeZone(baseTime - 150000, timezone, SEARCH_PARAMETER_DATE_FORMAT);\n    const to = formatInTimeZone(baseTime + 150000, timezone, SEARCH_PARAMETER_DATE_FORMAT);\n    const path = getTransactionListPath(\n      { applicationName: preferred.applicationName, serviceType: preferred.serviceType },\n      undefined,\n      serviceName,\n    );\n    const url = `${BASE_PATH}${path}?${convertParamsToQueryString({\n      from,\n      to,\n      traceInfo: traceId,\n      transactionInfo: JSON.stringify({\n        agentId: preferred.agentId,\n        spanId: preferred.spanId,","sourceCodeStart":762,"sourceCodeEnd":798,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/web-frontend/src/main/v3/packages/ui/src/components/Transaction/call-tree/callTreeTableColumns.tsx#L762-L798","documentation":"A toast warning shown when the OTEL trace-to-Pinpoint transaction link button cannot resolve the linked transaction. The code fetches the trace's span metadata rows; if no row has an applicationName or serviceType, it cannot build the transaction search URL, so it warns instead of navigating.","triggerScenarios":"Clicking the OTEL link button on a transaction whose trace metadata is empty, or whose resolved preferred row (span matching the link's spanId, else the first row) lacks applicationName or serviceType.","commonSituations":"OTLP traces ingested without application/service attributes; the linked spanId points to a sub-span whose row is missing; stale link to a trace that no longer has metadata rows; partially ingested trace data.","solutions":["Verify the trace metadata API returns rows with applicationName and serviceType populated for the given traceId/spanId","Check that the OTEL collector/pinpoint is mapping service.name and service.type resource attributes correctly","Retry the link after the trace is fully processed (data may still be ingesting)","Fall back to searching the transaction manually from the transaction list"],"exampleFix":"// before\nconst preferred = rows.find((row) => String(row.spanId) === spanId) || rows[0];\nif (!preferred?.applicationName || !preferred?.serviceType) {\n  toast.warn(t('TRANSACTION_LIST.LINKED_TRANSACTION_NOT_FOUND'));\n  return;\n}\n// after\nconst preferred = rows.find((row) => String(row.spanId) === spanId) || rows[0];\nif (!preferred?.applicationName || !preferred?.serviceType) {\n  toast.warn(t('TRANSACTION_LIST.LINKED_TRANSACTION_NOT_FOUND'));\n  // optionally: fall back to first row that HAS application info\n  const anyValid = rows.find((r) => r.applicationName && r.serviceType);\n  if (!anyValid) return;\n  preferred = anyValid;\n}","handlingStrategy":"fallback","validationCode":"const rows = data?.metadata ?? [];\nconst preferred = rows.find((r) => String(r.spanId) === spanId) || rows[0];\nconst linkable = !!preferred?.applicationName && !!preferred?.serviceType;","typeGuard":"const isLinkableRow = (row?: { applicationName?: string; serviceType?: string }): row is { applicationName: string; serviceType: string } =>\n  typeof row?.applicationName === 'string' && row.applicationName.length > 0 &&\n  typeof row?.serviceType === 'string' && row.serviceType.length > 0;","tryCatchPattern":null,"preventionTips":["Guard with optional chaining before reading preferred.collectorAcceptTime and other fields","Ensure OTEL exporters set service.name/service.type resource attributes","Handle empty trace metadata responses explicitly in the UI","Test link buttons against traces with missing or partial spans"],"tags":["react","frontend","opentelemetry","toast","missing-metadata"],"backgroundTag":"record-not-found","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}