caddyserver/caddy · error · APIError
encoding new config: %v
Error message
encoding new config: %v
What it means
After a successful mutation of the in-memory raw config, changeConfig re-encodes the whole config with json.Marshal. If any value in the raw config map cannot be represented as JSON (unsupported type injected by a module or plugin, NaN/Inf float, circular structure), marshaling fails and the request aborts with an APIError (HTTP 400) wrapping 'encoding new config'. The raw config is left mutated but not loaded.
Source
Thrown at caddy.go:215
if hex.EncodeToString(hash.Sum(nil)) != parts[1] {
return APIError{
HTTPStatus: http.StatusPreconditionFailed,
Err: fmt.Errorf("If-Match header did not match current config hash"),
}
}
}
err := unsyncedConfigAccess(method, path, input, nil)
if err != nil {
return err
}
// the mutation is complete, so encode the entire config as JSON
newCfg, err := json.Marshal(rawCfg[rawConfigKey])
if err != nil {
return APIError{
HTTPStatus: http.StatusBadRequest,
Err: fmt.Errorf("encoding new config: %v", err),
}
}
// if nothing changed, no need to do a whole reload unless the client forces it
if !forceReload && bytes.Equal(rawCfgJSON, newCfg) {
Log().Info("config is unchanged")
return errSameConfig
}
// find any IDs in this config and index them
idx := make(map[string]string)
err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
if err != nil {
if len(rawCfgJSON) > 0 {
var oldCfg any
err2 := json.Unmarshal(rawCfgJSON, &oldCfg)
if err2 != nil {
err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)View on GitHub (pinned to 50e54ee279)
Solutions
- Inspect the wrapped %v — it names the offending type/field that json.Marshal choked on.
- Remove or fix the module/plugin that injects non-serializable values into the config tree.
- If you control the code, store only JSON-compatible data (strings, numbers, bools, maps, slices) in raw config paths.
- Restart from a known-good config file (caddy run --config) to clear the mutated in-memory state.
Defensive patterns
Strategy: try-catch
Try / catch
err := changeConfig(...) // or admin POST
if err != nil {
if apiErr, ok := err.(caddy.APIError); ok && strings.Contains(apiErr.Error(), "encoding new config") {
// in-memory raw config mutated but not loaded: restart from file
log.Fatal("config serialization failed; restart from known-good file")
}
} Prevention
- Never inject non-JSON values (funcs, NaN, cycles) into raw config structures.
- Keep custom modules' stored config data strictly JSON-typed.
- Always keep the canonical config on disk so a restart can recover state.
When it happens
Trigger: A plugin or custom admin handler inserting non-JSON-serializable values (funcs, channels, NaN) into rawCfg before changeConfig re-encodes; extremely rare via stock Caddy because rawCfg originates from a JSON decode. JSON config paths (/config/, /id/) that programmatically set exotic values.
Common situations: Third-party modules hooking config storage; patched/forked Caddy builds; almost never seen with unmodified Caddy and file-based configs. When it does occur the culprit is usually a recently added custom module.
Related errors
- decoding request body: %w, at offset %d
- decoding request body: %w
- encoding config: %v
- final element is not an array
- marshaling %T matcher: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/6f7802859fbfa980.
Report an issue: GitHub.