caddyserver/caddy · error

marshaling ECH private key: %v

Error message

marshaling ECH private key: %v

What it means

When Caddy creates a new ECH config, it generates an X25519 HPKE key pair and serializes the private half with MarshalBinary before persisting it to storage under ech/configs/<id>/key.bin. For a freshly generated, in-memory X25519 key this call is effectively infallible in the crypto/ecdh-backed hpke library; the check is purely defensive. If it ever fires, it signals an internal invariant break or a library regression, not a user config problem.

Source

Thrown at modules/caddytls/ech.go:665

				AEADID: hpke.AEAD_AES128GCM,
			},
			{
				KDFID:  hpke.KDF_HKDF_SHA256,
				AEADID: hpke.AEAD_AES256GCM,
			},
			{
				KDFID:  hpke.KDF_HKDF_SHA256,
				AEADID: hpke.AEAD_ChaCha20Poly1305,
			},
		},
	}
	meta := echConfigMeta{
		Created: time.Now(),
	}

	privKeyBytes, err := privateKey.MarshalBinary()
	if err != nil {
		return echConfig{}, fmt.Errorf("marshaling ECH private key: %v", err)
	}
	echConfigBytes, err := echCfg.MarshalBinary()
	if err != nil {
		return echConfig{}, fmt.Errorf("marshaling ECH config: %v", err)
	}
	metaBytes, err := json.Marshal(meta)
	if err != nil {
		return echConfig{}, fmt.Errorf("marshaling ECH config metadata: %v", err)
	}

	parentKey := path.Join(echConfigsKey, strconv.Itoa(int(configID)))
	keyKey := path.Join(parentKey, "key.bin")
	configKey := path.Join(parentKey, "config.bin")
	metaKey := path.Join(parentKey, "meta.json")

	if err := ctx.Storage().Store(ctx, keyKey, privKeyBytes); err != nil {
		return echConfig{}, fmt.Errorf("storing ECH private key: %v", err)
	}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Treat as an internal error: report it upstream to Caddy with the Go and Caddy versions.
  2. Retry config load/start once to rule out a transient environment issue.
  3. Pin/rollback the Go toolchain or caddy version if the error appeared right after an upgrade.
Defensive patterns

Strategy: try-catch

Try / catch

if err != nil && strings.Contains(err.Error(), "marshaling ECH private key") {
    // internal invariant break: report, do not retry — same result guaranteed
    return fmt.Errorf("internal ECH key error, please report upstream: %w", err)
}

Prevention

When it happens

Trigger: Only reachable inside the internal routine that mints a new ECH config (privateKey just returned by hpke KEM_X25519_HKDF_SHA256.Scheme().GenerateKeyPair()). No user input influences it;MarshalBinary on a generated key does not fail in practice.

Common situations: Essentially never seen in the wild. Could appear after a Go or hpke dependency upgrade that changed key serialization semantics, or memory corruption.

Related errors


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