{"record":{"id":"fc1735ecb7140724","repo":"Budibase/budibase","slug":"workspace-id-is-required-to-configure-litellm","errorCode":null,"errorMessage":"Workspace ID is required to configure LiteLLM","messagePattern":"Workspace ID is required to configure LiteLLM","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/ai/configs/litellm.ts","lineNumber":390,"sourceCode":"  if (model.configType !== AIConfigType.COMPLETIONS) {\n    throw new HTTPError(`Unsupported AI config type: ${model.configType}`, 400)\n  }\n  return validateCompletionsModel(model)\n}\n\nexport async function getKeySettings(): Promise<{\n  keyId: string\n  secretKey: string\n  teamId: string\n}> {\n  const db = context.getWorkspaceDB()\n  const keyDocId = docIds.getLiteLLMKeyID()\n\n  let keyConfig = await db.tryGet<LiteLLMKeyConfig>(keyDocId)\n  if (!keyConfig || !keyConfig.teamId) {\n    const workspaceId = context.getProdWorkspaceId()\n    if (!workspaceId) {\n      throw new HTTPError(\"Workspace ID is required to configure LiteLLM\", 400)\n    }\n    const { result } = await locks.doWithLock(\n      {\n        name: LockName.LITELLM_KEY,\n        type: LockType.AUTO_EXTEND,\n        resource: workspaceId,\n      },\n      async () => {\n        let existingKeyConfig = await db.tryGet<LiteLLMKeyConfig>(keyDocId)\n        const shouldCreateTenantTeam = !existingKeyConfig?.teamId\n\n        if (existingKeyConfig && !shouldCreateTenantTeam) {\n          return existingKeyConfig\n        }\n\n        const team = await getOrCreateTenantTeam()\n\n        if (existingKeyConfig) {","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/ai/configs/litellm.ts#L372-L408","documentation":"getKeySettings lazily provisions the workspace's LiteLLM virtual key. It derives the lock/resource scope from context.getProdWorkspaceId(); when there is no prod workspace ID in the execution context (e.g. running at the global/platform level or inside a bare tenant DB), it cannot scope the key and throws HTTP 400 instead of creating a malformed key.","triggerScenarios":"Calling getKeySettings (or code paths that call it: create/update of AI configs) while the request context has no production workspace — e.g. no workspace header/doc context, running against the global DB, or a job executing outside any app/workspace context.","commonSituations":"Background scripts or tests that call LiteLLM helpers without establishing workspace context (ctx/workflow context not set); AI config CRUD invoked from tenant-level admin endpoints.","solutions":["Ensure the code runs inside a workspace context (context.withWorkspace / the request carries the app/workspace ID) before calling getKeySettings.","Pass an explicit workspace ID to the surrounding operation so context.getProdWorkspaceId() resolves.","For tests/scripts, wrap the call in the test helper that seeds prod workspace context."],"exampleFix":"// before\nawait getKeySettings() // no workspace context\n// after\nawait context.doInWorkspace(appId, async () => {\n  await getKeySettings()\n})","handlingStrategy":"validation","validationCode":"import { context } from \"@budibase/backend-core\"\nconst wsId = context.getProdWorkspaceId()\nif (!wsId) throw new Error(\"Call getKeySettings only inside a prod workspace context\")","typeGuard":null,"tryCatchPattern":"try {\n  await getKeySettings()\n} catch (e) {\n  if (e.status === 400 && e.message.includes(\"Workspace ID is required\")) {\n    // re-run wrapped in workspace context\n    return context.doInWorkspace(appId, getKeySettings)\n  }\n  throw e\n}","preventionTips":["Only invoke LiteLLM key helpers from request/job code that runs within a workspace context.","Assert workspace context exists at the top of scripts and tests.","Route key provisioning through app-scoped endpoints rather than global admin code."],"tags":["workspace-context","http-400","litellm","provisioning"],"backgroundTag":"missing-workspace-context","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}