{"record":{"id":"7ccfabb596418cfa","repo":"Budibase/budibase","slug":"error-syncing-keys-json-error-message","errorCode":null,"errorMessage":"Error syncing keys: ${json.error?.message}","messagePattern":"Error syncing keys: (.+?)","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/ai/configs/litellm.ts","lineNumber":473,"sourceCode":"      \"Content-Type\": \"application/json\",\n      Authorization: liteLLMAuthorizationHeader,\n    },\n    body: JSON.stringify({\n      key: keyId,\n      ...(modelIds ? { models: modelIds } : {}),\n      ...(vectorStoreIds ? { vector_store_ids: vectorStoreIds } : {}),\n      ...(teamId ? { team_id: teamId } : {}),\n    }),\n  }\n\n  const res = await fetch(`${liteLLMUrl}/key/update`, requestOptions)\n  const json = await res.json()\n  if (!res.ok) {\n    const message = [\"Error syncing keys\", json.error?.message]\n      .filter(Boolean)\n      .join(\": \")\n\n    throw new HTTPError(message, res.status || 400)\n  }\n}\n\nfunction isMissingVirtualKeyError(error: any): boolean {\n  const message = `${error?.message || \"\"}`.toLowerCase()\n  const status = error?.status\n\n  return status === 401 && message.includes(\"user key does not exist in db\")\n}\n\nasync function regenerateWorkspaceKey() {\n  const db = context.getWorkspaceDB()\n  const keyDocId = docIds.getLiteLLMKeyID()\n  const workspaceId = context.getProdWorkspaceId()\n\n  if (!workspaceId) {\n    throw new HTTPError(\"Workspace ID is required to configure LiteLLM\", 400)\n  }","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/ai/configs/litellm.ts#L455-L491","documentation":"updateKey calls the LiteLLM key management API and, on a non-OK response, joins the fixed prefix \"Error syncing keys\" with LiteLLM's own error message (json.error?.message) and rethrows as an HTTPError with the upstream status. It surfaces upstream LiteLLM proxy failures (auth, key limits, malformed request) to the caller.","triggerScenarios":"POST to the LiteLLM key endpoint returning 401/400/500 — e.g. wrong LITELLM_MASTER_KEY, key budget exceeded, LiteLLM proxy down or returning an error JSON body.","commonSituations":"LiteLLM proxy misconfigured or unreachable behind its URL env var; expired/rotated master key in the environment; model budget/max budget settings rejecting key creation.","solutions":["Read the appended json.error?.message in the thrown error for the upstream cause and fix that first.","Verify the LiteLLM master key / auth header configured for the server matches the LiteLLM proxy deployment.","Check that the LiteLLM proxy URL is reachable and healthy (curl its /health endpoint).","Retry the key sync after fixing upstream; the key doc may need regeneration via regenerateWorkspaceKey."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// pre-check proxy availability\nconst health = await fetch(`${liteLLMUrl}/health`)\nif (!health.ok) throw new Error(\"LiteLLM proxy unavailable before key sync\")","typeGuard":null,"tryCatchPattern":"try {\n  await updateKey({ keyId, keyName, models })\n} catch (e) {\n  // message is \"Error syncing keys: <upstream reason>\"\n  logger.error(\"LiteLLM key sync failed:\", e.message)\n  if (e.status === 401) throw new Error(\"Check LITELLM_MASTER_KEY configuration\")\n  throw e\n}","preventionTips":["Monitor LiteLLM proxy health and alert on downtime.","Keep master key/auth env vars in sync with the proxy deployment.","Log the full upstream message — it names the actual cause."],"tags":["network","litellm","upstream-error","http"],"backgroundTag":"upstream-api-error","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}