{"record":{"id":"51dab358c987c2a2","repo":"Budibase/budibase","slug":"config-id-is-required-for-updates","errorCode":null,"errorMessage":"Config ID is required for updates","messagePattern":"Config ID is required for updates","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/ai/configs.ts","lineNumber":113,"sourceCode":"\n    webSearchConfig: body.webSearchConfig,\n    configType: body.configType,\n    reasoningEffort: body.reasoningEffort,\n    isDefault: body.isDefault,\n  }\n\n  const newConfig = await sdk.ai.configs.create(createRequest)\n\n  ctx.body = await sanitizeConfig(newConfig)\n}\n\nexport const updateAIConfig = async (\n  ctx: UserCtx<UpdateAIConfigRequest, AIConfigResponse>\n) => {\n  const body = ctx.request.body\n\n  if (!body._id) {\n    throw new HTTPError(\"Config ID is required for updates\", 400)\n  }\n\n  if (!body._rev) {\n    throw new HTTPError(\"Revision is required for updates\", 400)\n  }\n\n  if (!body.name) {\n    throw new HTTPError(\"Config name is required\", 400)\n  }\n\n  const configType = body.configType ?? AIConfigType.COMPLETIONS\n\n  const updateRequest: RequiredKeys<\n    RequiredKeys<Parameters<typeof sdk.ai.configs.update>[0]>\n  > = {\n    _id: body._id,\n    _rev: body._rev,\n    name: body.name,","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/ai/configs.ts#L95-L131","documentation":"updateAIConfig performs a CouchDB-style document update and therefore requires the document _id on the body. Without _id it cannot identify which config to update, so it returns 400 'Config ID is required for updates'.","triggerScenarios":"PUT /api/ai/configs with a body lacking _id (e.g. posting a create-shaped payload to the update endpoint); _id lost after stripping document metadata when cloning a config.","commonSituations":"Client fetches a config, deletes all CouchDB meta fields, then PUTs it back; API consumers confuse the create and update endpoints; mapping the body through a narrower type that drops _id.","solutions":["Include the config's _id in the PUT body (fetch the config first if needed).","Preserve CouchDB metadata (_id, _rev) when transforming/submitting config objects.","Use POST /api/ai/configs to create new configs instead of PUT.","If the id is only known as a route param, merge it into the body before sending."],"exampleFix":"// before\nawait api.put(\"/api/ai/configs\", { name: \"Renamed\", _rev: config._rev })\n// after\nawait api.put(\"/api/ai/configs\", { _id: config._id, _rev: config._rev, name: \"Renamed\" })","handlingStrategy":"validation","validationCode":"if (!config._id) throw new Error(\"Config _id is required before calling update\")","typeGuard":"const hasId = (c: { _id?: string }): c is { _id: string } =>\n  typeof c._id === \"string\" && c._id.length > 0","tryCatchPattern":"try {\n  await api.put(\"/api/ai/configs\", body)\n} catch (e) {\n  if (e.status === 400 && e.message.includes(\"Config ID is required\")) {\n    // fetch the config by some external key, merge _id/_rev, retry\n  }\n}","preventionTips":["Always fetch the existing config before updating so CouchDB metadata is present","Never strip _id/_rev from documents before PUT","Use create (POST) for new docs, update (PUT) only for existing docs","Model update payloads as full document types, not partials"],"tags":["http-400","validation","ai-configs","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}