caddyserver/caddy · error

generating unique config ID: %v

Error message

generating unique config ID: %v

What it means

Caddy's ECH (Encrypted ClientHello) support generates a new ECH config by first allocating a unique 1-byte config ID via newECHConfigID. That helper probes the configured storage (path ech/configs/<id>) and also honors context cancellation. This error wraps any failure from that probe: a storage backend error (Exists failing) or a cancelled/shut-down context. It occurs during provisioning of tls.caddytls ECH when no usable ECH config exists yet.

Source

Thrown at modules/caddytls/ech.go:635

	// must match the config provided to clients byte-for-byte. The config
	// should only specify the DHKEM(X25519, HKDF-SHA256) KEM ID (0x0020), the
	// HKDF-SHA256 KDF ID (0x0001), and a subset of the following AEAD IDs:
	// AES-128-GCM (0x0001), AES-256-GCM (0x0002), ChaCha20Poly1305 (0x0003)."
	//
	// So we need to be sure we generate a config within these parameters
	// so the Go TLS server can use it.

	// generate a key pair
	const kemChoice = hpke.KEM_X25519_HKDF_SHA256
	publicKey, privateKey, err := kemChoice.Scheme().GenerateKeyPair()
	if err != nil {
		return echConfig{}, err
	}

	// find an available config ID
	configID, err := newECHConfigID(ctx)
	if err != nil {
		return echConfig{}, fmt.Errorf("generating unique config ID: %v", err)
	}

	echCfg := echConfig{
		PublicKey:     publicKey,
		Version:       draftTLSESNI25,
		ConfigID:      configID,
		RawPublicName: publicName,
		KEMID:         kemChoice,
		CipherSuites: []hpkeSymmetricCipherSuite{
			{
				KDFID:  hpke.KDF_HKDF_SHA256,
				AEADID: hpke.AEAD_AES128GCM,
			},
			{
				KDFID:  hpke.KDF_HKDF_SHA256,
				AEADID: hpke.AEAD_AES256GCM,
			},
			{

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Check that the configured storage module works: run caddy again and look at earlier storage errors; verify permissions/connectivity of the storage backend.
  2. Ensure the storage directory (default $HOME/.local/share/caddy) is writable by the Caddy user.
  3. If the context was cancelled (shutdown mid-start), simply restart Caddy; the error is transient.
  4. Clear stale ECH state (ech/configs/* in storage) if a previous partial write corrupted the ID space.
Defensive patterns

Strategy: validation

Validate before calling

// Before starting Caddy with ECH, verify the storage prefix is reachable.
func checkECHStorage(stor caddy.Storage, ctx context.Context) error {
    ok, err := stor.Exists(ctx, "ech/configs")
    if err != nil {
        return fmt.Errorf("storage unusable for ECH: %w", err)
    }
    _ = ok
    return nil
}

Try / catch

// In Go code embedding Caddy, treat this as fatal for the config load:
if err := cfg.AutomaticHTTPS or provisioning; err != nil && strings.Contains(err.Error(), "generating unique config ID") {
    log.Printf("ECH provisioning failed (storage or shutdown): %v", err)
}

Prevention

When it happens

Trigger: Calling Provision/start of the tls app with ECH enabled (ech > on) while: (1) the storage backend errors on Exists (bad permissions, unreachable distributed storage), or (2) the caddy.Context is cancelled (shutdown during config start, timeout).

Common situations: Caddyfile/JSON config with ECH enabled and a misconfigured storage module (e.g. s3 credentials rejected, file_storage path read-only); starting many configs concurrently and one gets cancelled; upgrading storage backends so old paths are inaccessible.

Related errors


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