{"record":{"id":"0470f38e3475bf7a","repo":"langfuse/langfuse","slug":"invalid-request-0470f3","errorCode":"invalid_request","errorMessage":"params.message (invalid widget request message)","messagePattern":"params\\.message \\(invalid widget request message\\)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"web/src/features/widgets/server/public-dashboard-widget-service.ts","lineNumber":45,"sourceCode":"} from \"@/src/features/dashboard/lib/dashboardUiTableToViewMapping\";\nimport {\n  MAX_PIVOT_TABLE_DIMENSIONS,\n  MAX_PIVOT_TABLE_METRICS,\n} from \"@/src/features/widgets/utils/pivot-table-utils\";\n\n// The widget shape used internally after input normalization: the public\n// body with chartConfig and filters fully resolved.\ntype NormalizedWidgetInput = Omit<\n  z.infer<typeof PostUnstableDashboardWidgetResponse>,\n  \"id\" | \"createdAt\" | \"updatedAt\"\n>;\n\nconst throwInvalidWidget = (params: {\n  message: string;\n  field?: string;\n  allowedValues?: string[];\n}): never => {\n  throw createStructuredPublicApiError({\n    httpCode: 400,\n    code: \"invalid_request\",\n    message: params.message,\n    details:\n      params.field || params.allowedValues\n        ? {\n            field: params.field,\n            allowedValues: params.allowedValues,\n          }\n        : undefined,\n  });\n};\n\nfunction resolvePublicWidgetPersistedViewVersion(\n  widget: NormalizedWidgetInput,\n  persistedMinVersion?: number,\n): ViewVersion {\n  return resolveDashboardWidgetMinVersion(","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/langfuse/langfuse/blob/59d92c7cf365150d10b753b5a0d1708902a2ed60/web/src/features/widgets/server/public-dashboard-widget-service.ts#L27-L63","documentation":"throwInvalidWidget is the shared 400 invalid_request factory for public dashboard widget requests (getPublicDashboardWidgetViewDeclaration, input normalization/validation). The message is whatever the caller passes — typically an invalid widget type, filter, or field value.","triggerScenarios":"Requesting a public dashboard widget whose 'type' is not one of the allowed widget types, or passing invalid filters/pagination in the widget query payload.","commonSituations":"Frontend deployed ahead of backend (new widget type not yet supported), stale widget config persisted with a removed type, or hand-crafted API calls with wrong enum values.","solutions":["Read the message/details — it names the invalid field and (when applicable) allowedValues.","Use only widget types returned by the dashboard/widget declaration endpoint.","After a Langfuse upgrade, clear or migrate persisted widget configs referencing removed types."],"exampleFix":"// before\nGET /api/public/dashboard-widgets?type=custom_funnel\n// after\nGET /api/public/dashboard-widgets?type=traces-timeseries","handlingStrategy":"validation","validationCode":"const WIDGET_TYPES = [/* from declaration endpoint */];\nif (!WIDGET_TYPES.includes(widget.type)) throw new Error(`Unknown widget type ${widget.type}`);","typeGuard":"const isKnownWidgetType = (t: string): t is WidgetType => WIDGET_TYPES.includes(t as WidgetType);","tryCatchPattern":"catch (e) { if (e?.code === \"invalid_request\" && e.details?.allowedValues) restrictSelectorTo(e.details.allowedValues); }","preventionTips":["Drive widget type pickers from the server-declared list","Re-validate persisted widget configs after upgrades"],"tags":["widgets","dashboard","validation","public-api"],"backgroundTag":"invalid-enum-value","analyzedSha":"59d92c7cf365150d10b753b5a0d1708902a2ed60","analyzedAt":"2026-08-27T22:22:00.402Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}