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

  1. Inspect the wrapped %v — it names the offending type/field that json.Marshal choked on.
  2. Remove or fix the module/plugin that injects non-serializable values into the config tree.
  3. If you control the code, store only JSON-compatible data (strings, numbers, bools, maps, slices) in raw config paths.
  4. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/6f7802859fbfa980. Report an issue: GitHub.