{"record":{"id":"253ea03dee9dd86a","repo":"Budibase/budibase","slug":"userid-is-required","errorCode":null,"errorMessage":"userId is required","messagePattern":"userId is required","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/ai/chatConversations.ts","lineNumber":48,"sourceCode":"import sdk from \"../../../sdk\"\nimport { isDevWorkspaceID } from \"../../../db/utils\"\nimport {\n  buildAgentMessageUsage,\n  formatIncompleteToolCallError,\n  prepareAgentChatRun,\n  type AgentChatRun,\n} from \"../../../sdk/workspace/ai/agents\"\nimport { sdk as usersSdk } from \"@budibase/shared-core\"\nimport { truncateTitle } from \"../../../sdk/workspace/ai/chatConversations\"\nimport {\n  determineTrigger,\n  resolvePreviewSessionId,\n} from \"../../../sdk/workspace/ai/agentLogs/shared\"\n\nconst getGlobalUserId = (ctx: UserCtx) => {\n  const userId = ctx.user?.globalId || ctx.user?.userId || ctx.user?._id\n  if (!userId) {\n    throw new HTTPError(\"userId is required\", 400)\n  }\n  return userId as string\n}\n\nconst getRecentChatContext = (\n  messages: ChatConversation[\"messages\"],\n  limit = 6\n) => {\n  return messages\n    .flatMap(message => {\n      if (message.role !== \"user\" && message.role !== \"assistant\") {\n        return []\n      }\n\n      const content = (message.parts || [])\n        .filter(\n          (\n            part","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/chatConversations.ts#L30-L66","documentation":"getGlobalUserId derives the caller's id from ctx.user (globalId || userId || _id). If the authenticated context carries no user or none of those fields, the endpoint cannot attribute the chat conversation and throws HTTP 400 'userId is required'.","triggerScenarios":"Calling chat conversation endpoints (e.g. recent conversations) with an unauthenticated or malformed session token so ctx.user has no globalId/userId/_id.","commonSituations":"Expired or invalid JWT still passing middleware but with a stripped payload; internal service calls without user context; API keys used where a user session is expected.","solutions":["Ensure the request carries a valid authenticated user session/JWT","Check ctx.user population in auth middleware — globalId should be present for SSO/JWT users","For service-to-service calls, pass an explicit userId or use a user-scoped API key"],"exampleFix":"// before\nfetch('/api/ai/chatconversations/recent') // no auth header\n// after\nfetch('/api/ai/chatconversations/recent', {\n  headers: { Authorization: `Bearer ${token}` }\n})","handlingStrategy":"validation","validationCode":"if (!user || !(user.globalId || user.userId || user._id)) {\n  throw new Error(\"Authenticated user with an id is required\")\n}","typeGuard":"const hasUserId = (u: unknown): u is { globalId: string } =>\n  !!u && typeof u === \"object\" &&\n  typeof (u as any).globalId === \"string\" && (u as any).globalId !== \"\"","tryCatchPattern":"try {\n  await api.getRecentConversations()\n} catch (e) {\n  if (e.status === 400 && String(e.message).includes(\"userId is required\")) {\n    // re-authenticate the user session before retrying\n  }\n}","preventionTips":["Always attach a valid auth token to AI chat API calls","Check token expiry and refresh before requests","Use user-scoped credentials, not bare service keys, for conversation endpoints"],"tags":["auth","validation","http-400"],"backgroundTag":"missing-user-context","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}