{"record":{"id":"5cf80b7157dfc007","repo":"calcom/cal.diy","slug":"error-message-as-trpcerrorcode","errorCode":null,"errorMessage":"${error.message as TRPCErrorCode}","messagePattern":"\\$\\{error\\.message as TRPCErrorCode\\}","errorType":"http","errorClass":"TRPCError","httpStatus":null,"severity":"error","filePath":"apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.ts","lineNumber":223,"sourceCode":"        query.timeZone\n      );\n\n      return {\n        data: {\n          slots,\n        },\n        status: SUCCESS_STATUS,\n      };\n    } catch (error) {\n      if (error instanceof Error) {\n        if (error.message.includes(\"Invalid time range given\")) {\n          throw new BadRequestException(\n            \"Invalid time range given - check the 'startTime' and 'endTime' query parameters.\"\n          );\n        }\n\n        if (TRPC_ERROR_MAP[error.message as keyof typeof TRPC_ERROR_CODE]) {\n          throw new TRPCError({ code: error.message as TRPCErrorCode });\n        }\n      }\n\n      throw error;\n    }\n  }\n}\n","sourceCodeStart":205,"sourceCodeEnd":231,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.ts#L205-L231","documentation":"Re-thrown as a TRPCError whose code is the upstream error.message, but only when that message is a key present in TRPC_ERROR_MAP (i.e. a known tRPC error code). The slots controller bridges an underlying tRPC-layer error into the Nest response pipeline by mapping its textual code back into a structured TRPCError. If the message is not in the map, control falls through to the final `throw error`.","triggerScenarios":"getAvailableSlots throws a tRPC-style error message such as 'BAD_REQUEST', 'UNAUTHORIZED', 'FORBIDDEN', 'NOT_FOUND', 'INTERNAL_SERVER_ERROR', etc. — any key of TRPC_ERROR_CODE — which the controller then lifts into a TRPCError for the global trpc-exception filter to render.","commonSituations":"Reusing core booking/slot tRPC procedures from the API v2 controller; the shared procedure throws a typed tRPC error and the Nest wrapper must translate it; version drift where a new tRPC code is thrown but TRPC_ERROR_MAP has not been updated.","solutions":["Handle the upstream cause: a BAD_REQUEST means bad inputs, UNAUTHORIZED means auth — fix the triggering condition rather than the wrapper.","If a legitimate new tRPC code is not in TRPC_ERROR_MAP, add it to the map so it translates instead of falling through to a raw 500.","Catch TRPCError in your client and switch on error.code to drive UX."],"exampleFix":"// before — new code thrown but not mapped, surfaces as raw error\n// (TRPC_ERROR_MAP missing 'PAYLOAD_TOO_LARGE')\n// after — extend the map\nTRPC_ERROR_MAP = { ...TRPC_ERROR_MAP, PAYLOAD_TOO_LARGE: 'PAYLOAD_TOO_LARGE' } as const;\n// client:\ntry { await api.getSlots(q); }\ncatch (e) { if (e.code === 'UNAUTHORIZED') redirectToLogin(); }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isTrpcCode = (code: unknown): boolean =>\n  typeof code === 'string' &&\n  ['BAD_REQUEST','UNAUTHORIZED','FORBIDDEN','NOT_FOUND','TIMEOUT','CONFLICT','INTERNAL_SERVER_ERROR'].includes(code);","tryCatchPattern":"try { await api.getSlots(q); }\ncatch (e) {\n  if (isTrpcCode(e.code)) {\n    switch (e.code) {\n      case 'UNAUTHORIZED': redirectToLogin(); break;\n      case 'NOT_FOUND': showNotFound(); break;\n      default: throw e;\n    }\n  } else throw e;\n}","preventionTips":["Keep TRPC_ERROR_MAP in sync with the tRPC codes your shared procedures can throw.","On the client, switch on error.code for UX rather than parsing messages.","Add a regression test whenever a new tRPC code is introduced upstream."],"tags":["slots","trpc","error-mapping","nestjs","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}