{"record":{"id":"eb4877cf1c19371b","repo":"caddyserver/caddy","slug":"v-additionally-restoring-old-config-v","errorCode":null,"errorMessage":"%v; additionally, restoring old config: %v","messagePattern":"(.+?); additionally, restoring old config: (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"caddy.go","lineNumber":233,"sourceCode":"\t\t\tErr:        fmt.Errorf(\"encoding new config: %v\", err),\n\t\t}\n\t}\n\n\t// if nothing changed, no need to do a whole reload unless the client forces it\n\tif !forceReload && bytes.Equal(rawCfgJSON, newCfg) {\n\t\tLog().Info(\"config is unchanged\")\n\t\treturn errSameConfig\n\t}\n\n\t// find any IDs in this config and index them\n\tidx := make(map[string]string)\n\terr = indexConfigObjects(rawCfg[rawConfigKey], \"/\"+rawConfigKey, idx)\n\tif err != nil {\n\t\tif len(rawCfgJSON) > 0 {\n\t\t\tvar oldCfg any\n\t\t\terr2 := json.Unmarshal(rawCfgJSON, &oldCfg)\n\t\t\tif err2 != nil {\n\t\t\t\terr = fmt.Errorf(\"%v; additionally, restoring old config: %v\", err, err2)\n\t\t\t}\n\t\t\trawCfg[rawConfigKey] = oldCfg\n\t\t} else {\n\t\t\trawCfg[rawConfigKey] = nil\n\t\t}\n\t\treturn APIError{\n\t\t\tHTTPStatus: http.StatusBadRequest,\n\t\t\tErr:        fmt.Errorf(\"indexing config: %v\", err),\n\t\t}\n\t}\n\n\t// load this new config; if it fails, we need to revert to\n\t// our old representation of caddy's actual config\n\terr = unsyncedDecodeAndRun(newCfg, true)\n\tif err != nil {\n\t\tif len(rawCfgJSON) > 0 {\n\t\t\t// restore old config state to keep it consistent\n\t\t\t// with what caddy is still running; we need to","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddy.go#L215-L251","documentation":"While indexing the new config's @id fields (indexConfigObjects), a failure occurred, and the recovery path tried to restore the previous config by unmarshaling the stored rawCfgJSON — which also failed. This compound error reports both the original indexing error and the restore failure; the in-memory raw config may now be inconsistent with the running config until the next successful load.","triggerScenarios":"A config change whose @id handling fails (duplicate IDs, wrong @id type) while the previously stored encoded config has become unmarshalable — essentially only possible when rawCfgJSON was corrupted by faulty custom code, since it was produced by json.Marshal. Returned as the inner error of the 'indexing config' APIError.","commonSituations":"Forked Caddy or plugins manipulating rawCfgJSON/rawCfg directly; memory corruption-class bugs; practically unreachable in stock Caddy. Seeing it means both the change and the rollback path are broken.","solutions":["Read both messages: the first is the indexing failure (duplicate/invalid @id), the second the restore failure.","Fix the @id problem in the submitted config (uniqueness, string/number values).","Restart Caddy from a known-good config file to rebuild consistent in-memory state — the process may hold a mutated rawCfg.","Audit any custom code that touches rawCfg/rawCfgJSON, as stock Caddy cannot produce the second failure."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := postLoad(cfg); err != nil && strings.Contains(err.Error(), \"additionally, restoring old config\") {\n    // both the change AND rollback failed: state is suspect\n    log.Fatal(\"config index + rollback failed; restart Caddy from file\")\n}","preventionTips":["Do not manipulate caddy's rawCfg/rawCfgJSON from plugins.","Fix @id errors at the source (see errors 69/72/73) — they are what trigger this path.","Monitor admin API errors in CI config pushes to catch compound failures early."],"tags":["caddy","admin-api","config","error-recovery","id-index"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}