caddyserver/caddy · warning · PublishECHConfigListErrors

unable to publish ECH data to HTTPS DNS record: %w (zone=%s

Error message

unable to publish ECH data to HTTPS DNS record: %w (zone=%s dns_record_name=%s)

What it means

The final write of ECH DNS publishing: dnsPub.provider.SetRecords sets the HTTPS ServiceBinding record (priority 2, target '.', TTL 5m, ech SvcParam merged with existing params). Failure is wrapped with zone and record name context and collected into PublishECHConfigListErrors for that domain. Causes are provider-side: auth, permissions, zone mismatch, rate limits, or API errors.

Source

Thrown at modules/caddytls/ech.go:895

			continue
		}
		params := httpsRec.Params
		params = dnsPub.publishedSvcParams(domain, params, configListBin)

		// publish record
		_, err = dnsPub.provider.SetRecords(ctx, zone, []libdns.Record{
			libdns.ServiceBinding{
				// HTTPS and SVCB RRs: RFC 9460 (https://www.rfc-editor.org/rfc/rfc9460)
				Scheme:   "https",
				Name:     relName,
				TTL:      5 * time.Minute, // TODO: low hard-coded value only temporary; change to a higher value once more field-tested and key rotation is implemented
				Priority: 2,               // allows a manual override with priority 1
				Target:   ".",
				Params:   params,
			},
		})
		if err != nil {
			errs[domain] = fmt.Errorf("unable to publish ECH data to HTTPS DNS record: %w (zone=%s dns_record_name=%s)", err, zone, relName)
			continue
		}
	}

	if len(errs) > 0 {
		return errs
	}
	return nil
}

func (dnsPub *ECHDNSPublisher) publishedSvcParams(domain string, existing libdns.SvcParams, configListBin []byte) libdns.SvcParams {
	params := make(libdns.SvcParams, len(existing)+2)
	for key, values := range existing {
		params[key] = append([]string(nil), values...)
	}

	params["ech"] = []string{base64.StdEncoding.EncodeToString(configListBin)}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use the zone= and dns_record_name= in the message plus the wrapped error to pinpoint the rejected write.
  2. Grant the token write access for that zone (or move DNS hosting to the configured provider).
  3. Retry after rate-limit windows; spread publications if many domains.
  4. Verify manually that the API token can create HTTPS/SVCB records via the provider's console.
Defensive patterns

Strategy: retry

Try / catch

var perrs caddytls.PublishECHConfigListErrors
if errors.As(err, &perrs) {
    for d, e := range perrs {
        if strings.Contains(e.Error(), "unable to publish ECH data") {
            // zone/record named in message; fix token write scope or zone hosting, then reload
        }
    }
}

Prevention

When it happens

Trigger: SetRecords for <relName> HTTPS RR in <zone> rejected: token lacks write scope, zone not managed by that provider account, provider API 4xx/5xx, concurrent modification conflict.

Common situations: Read-only DNS token; domain's DNS hosted at a different provider than configured; publishing during a provider outage; very long record sets exceeding provider limits.

Related errors


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