{"record":{"id":"7d5761bbf298264c","repo":"caddyserver/caddy","slug":"s-key-already-exists-s","errorCode":null,"errorMessage":"[%s] key already exists: %s","messagePattern":"\\[(.+?)\\] key already exists: (.+?)","errorType":"http","errorClass":"APIError","httpStatus":409,"severity":"error","filePath":"admin.go","lineNumber":1285,"sourceCode":"\t\t\t\t\t// it, otherwise it just sets or creates the value\n\t\t\t\t\tif arr, ok := v[part].([]any); ok {\n\t\t\t\t\t\tif ellipses {\n\t\t\t\t\t\t\tvalArray, ok := val.([]any)\n\t\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\t\treturn fmt.Errorf(\"final element is not an array\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tv[part] = append(arr, valArray...)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tv[part] = append(arr, val)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tv[part] = val\n\t\t\t\t\t}\n\t\t\t\tcase http.MethodPut:\n\t\t\t\t\tif _, ok := v[part]; ok {\n\t\t\t\t\t\treturn APIError{\n\t\t\t\t\t\t\tHTTPStatus: http.StatusConflict,\n\t\t\t\t\t\t\tErr:        fmt.Errorf(\"[%s] key already exists: %s\", path, part),\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tv[part] = val\n\t\t\t\tcase http.MethodPatch:\n\t\t\t\t\tif _, ok := v[part]; !ok {\n\t\t\t\t\t\treturn APIError{\n\t\t\t\t\t\t\tHTTPStatus: http.StatusNotFound,\n\t\t\t\t\t\t\tErr:        fmt.Errorf(\"[%s] key does not exist: %s\", path, part),\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tv[part] = val\n\t\t\t\tcase http.MethodDelete:\n\t\t\t\t\tif _, ok := v[part]; !ok {\n\t\t\t\t\t\treturn APIError{\n\t\t\t\t\t\t\tHTTPStatus: http.StatusNotFound,\n\t\t\t\t\t\t\tErr:        fmt.Errorf(\"[%s] key does not exist: %s\", path, part),\n\t\t\t\t\t\t}\n\t\t\t\t\t}","sourceCodeStart":1267,"sourceCodeEnd":1303,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/admin.go#L1267-L1303","documentation":"Returned (wrapped in APIError, HTTP 409 Conflict) when PUT targets a map key that already exists. PUT in Caddy's admin API is strictly create-only: the final path segment must not already be present in the map. POST is the create-or-append verb, PATCH the update verb.","triggerScenarios":"PUT /config/apps/http/servers/myserver2 when 'myserver2' already exists; PUT any .../key where key is present. Prior creation by another client or an earlier run of the same script.","commonSituations":"Idempotency-minded scripts using PUT as upsert; re-running a bootstrap script that provisions the same key twice; race between two clients creating the same resource.","solutions":["If the key should be replaced, use PATCH (update existing) or DELETE then PUT","If it should be created once, treat 409 as 'already done' and skip","Use POST for append-or-create semantics on list endpoints"],"exampleFix":"# before\ncurl -X PUT http://localhost:2019/config/apps/http/servers/myserver2 -d '{...}'  # 409\n# after: replace existing\ncurl -X PATCH http://localhost:2019/config/apps/http/servers/myserver2 -d '{...}'","handlingStrategy":"fallback","validationCode":"key=\"myserver2\"\nexists=$(curl -s \"http://localhost:2019/config/apps/http/servers\" | jq --arg k \"$key\" 'has($k)')\n[ \"$exists\" = \"false\" ] && curl -X PUT \"http://localhost:2019/config/apps/http/servers/$key\" -d @body.json","typeGuard":null,"tryCatchPattern":"On 409 from PUT, decide intent: skip (already provisioned), PATCH to overwrite, or DELETE+PUT to reset. Never blind-retry the same PUT.","preventionTips":["PUT = create-only, PATCH = update-only, POST = append/create in Caddy's API","Make bootstrap scripts treat 409 as success for idempotent runs"],"tags":["caddy","admin-api","config","conflict","http-409"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}