{"record":{"id":"8a1ffb4f680a281e","repo":"Budibase/budibase","slug":"preview-role-not-found","errorCode":null,"errorMessage":"Preview role not found","messagePattern":"Preview role not found","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/ai/chatConversations.ts","lineNumber":402,"sourceCode":"  )\n\n  if (chat.isPreview !== true) {\n    throw new HTTPError(\"Preview mode is required\", 400)\n  }\n\n  if (!isBuilderOrAdmin) {\n    throw new HTTPError(\"Forbidden\", 403)\n  }\n\n  if (!isDevWorkspaceID(workspaceId)) {\n    throw new HTTPError(\"Preview mode requires a development workspace\", 400)\n  }\n\n  let user = ctx.user\n  if (chat.previewRoleId) {\n    const previewRole = await roles.getRole(chat.previewRoleId)\n    if (!previewRole?._id) {\n      throw new HTTPError(\"Preview role not found\", 400)\n    }\n    user = {\n      ...ctx.user,\n      roleId: previewRole._id,\n    }\n  }\n\n  const agentId = chat.agentId\n  if (!agentId) {\n    throw new HTTPError(\"agentId is required\", 400)\n  }\n\n  return {\n    agentId,\n    chat,\n    userId,\n    user,\n  }","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/chatConversations.ts#L384-L420","documentation":"Preview chat allows impersonating a role via body.previewRoleId. The server fetches that role with roles.getRole; if the role doesn't exist (deleted or wrong workspace) the lookup yields no _id and the server throws HTTP 400 'Preview role not found'.","triggerScenarios":"POST chat stream with previewRoleId referencing a role id that is absent in the current dev workspace — e.g. a role copied from another app or deleted after the body was cached.","commonSituations":"Stale previewRoleId cached in the client after roles changed; copying request payloads between apps with different role ids; typos in role ids.","solutions":["Fetch the current role list for the workspace and use a valid _id for previewRoleId","Omit previewRoleId to chat under the caller's own role","Clear cached client state after role changes"],"exampleFix":"// before\n{ \"previewRoleId\": \"role_deleted_123\", ... }\n// after\n{ \"previewRoleId\": null, ... } // or a valid role _id","handlingStrategy":"validation","validationCode":"const role = await fetchRole(appId, previewRoleId)\nif (!role?._id) throw new Error(\"previewRoleId does not exist in this workspace\")","typeGuard":"const isValidRole = (r: { _id?: string } | null | undefined): r is { _id: string } =>\n  typeof r?._id === \"string\" && r._id.length > 0","tryCatchPattern":"try {\n  await streamChat(body)\n} catch (e) {\n  if (e.status === 400 && String(e.message).includes(\"Preview role not found\")) {\n    // drop previewRoleId or pick a valid role, then retry\n  }\n}","preventionTips":["Refresh cached role ids whenever roles are edited","Validate previewRoleId against the workspace role list before sending","Omit previewRoleId when role impersonation isn't needed"],"tags":["validation","http-400","roles"],"backgroundTag":"invalid-resource-reference","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}