caddyserver/caddy · error

loading new config: %v

Error message

loading new config: %v

What it means

The new config was accepted by changeConfig but unsyncedDecodeAndRun failed while preparing or running it — module loading, provisioning, validation, or app startup returned an error. Caddy restores the previous raw config and returns 'loading new config' wrapping the root cause. The old config keeps running, making admin-API loads atomic.

Source

Thrown at caddy.go:264

	// our old representation of caddy's actual config
	err = unsyncedDecodeAndRun(newCfg, true)
	if err != nil {
		if len(rawCfgJSON) > 0 {
			// restore old config state to keep it consistent
			// with what caddy is still running; we need to
			// unmarshal it again because it's likely that
			// pointers deep in our rawCfg map were modified
			var oldCfg any
			err2 := json.Unmarshal(rawCfgJSON, &oldCfg)
			if err2 != nil {
				err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
			}
			rawCfg[rawConfigKey] = oldCfg
		} else {
			rawCfg[rawConfigKey] = nil
		}

		return fmt.Errorf("loading new config: %v", err)
	}

	// success, so update our stored copy of the encoded
	// config to keep it consistent with what caddy is now
	// running (storing an encoded copy is not strictly
	// necessary, but avoids an extra json.Marshal for
	// each config change)
	rawCfgJSON = newCfg
	rawCfgIndex = idx

	return nil
}

// readConfig traverses the current config to path
// and writes its JSON encoding to out.
func readConfig(path string, out io.Writer) error {
	rawCfgMu.RLock()
	defer rawCfgMu.RUnlock()

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Read the wrapped error — it identifies the failing module, field, or listener precisely.
  2. Pre-validate before applying: caddy validate --config <file> (add --adapter if not JSON).
  3. Fix the named module/field (correct module ID, free the port, fix TLS settings) and retry the load.
  4. For custom builds, ensure the required plugin is compiled in (caddy list-modules | grep <name>).

Example fix

# before
curl -X POST localhost:2019/load -H 'Content-Type: application/json' --data @broken.json

# after: validate first, then load
caddy validate --config caddy.json && curl -X POST localhost:2019/load -H 'Content-Type: application/json' --data @caddy.json
Defensive patterns

Strategy: try-catch

Validate before calling

// Dry-run the exact bytes you will POST.
if err := caddy.Validate(cfgBytes); err != nil { // or: caddy validate --config file
    return err
}

Try / catch

resp, err := client.Post("http://localhost:2019/load", "application/json", bytes.NewReader(cfg))
if err == nil && resp.StatusCode >= 400 {
    body, _ := io.ReadAll(resp.Body)
    if strings.Contains(string(body), "loading new config") {
        // old config still running; fix the named module/field and retry
    }
}

Prevention

When it happens

Trigger: POST /load (or PUT/PATCH /config/) with a config that references an unregistered module ID, fails Validate(), binds a port already in use, has invalid TLS/automation policies, or whose app Start() errors. Caddyfile users see the equivalent from 'caddy validate' failures.

Common situations: Deploying a config that uses a module not compiled into the custom build; port conflicts on reload; invalid ACME/TLS settings; schema changes between Caddy versions (renamed fields produce provisioning errors).

Related errors


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