{"record":{"id":"51c56e523a815b2c","repo":"caddyserver/caddy","slug":"storing-ech-config-metadata-v","errorCode":null,"errorMessage":"storing ECH config metadata: %v","messagePattern":"storing ECH config metadata: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/ech.go","lineNumber":688,"sourceCode":"\t}\n\tmetaBytes, err := json.Marshal(meta)\n\tif err != nil {\n\t\treturn echConfig{}, fmt.Errorf(\"marshaling ECH config metadata: %v\", err)\n\t}\n\n\tparentKey := path.Join(echConfigsKey, strconv.Itoa(int(configID)))\n\tkeyKey := path.Join(parentKey, \"key.bin\")\n\tconfigKey := path.Join(parentKey, \"config.bin\")\n\tmetaKey := path.Join(parentKey, \"meta.json\")\n\n\tif err := ctx.Storage().Store(ctx, keyKey, privKeyBytes); err != nil {\n\t\treturn echConfig{}, fmt.Errorf(\"storing ECH private key: %v\", err)\n\t}\n\tif err := ctx.Storage().Store(ctx, configKey, echConfigBytes); err != nil {\n\t\treturn echConfig{}, fmt.Errorf(\"storing ECH config: %v\", err)\n\t}\n\tif err := ctx.Storage().Store(ctx, metaKey, metaBytes); err != nil {\n\t\treturn echConfig{}, fmt.Errorf(\"storing ECH config metadata: %v\", err)\n\t}\n\n\techCfg.privKeyBin = privKeyBytes\n\techCfg.configBin = echConfigBytes // this contains the public key\n\techCfg.meta = meta\n\n\treturn echCfg, nil\n}\n\n// ECH represents an Encrypted ClientHello configuration.\n//\n// EXPERIMENTAL: Subject to change.\ntype ECHConfiguration struct {\n\t// The public server name (SNI) that will be used in the outer ClientHello.\n\t// This should be a domain name for which this server is authoritative,\n\t// because Caddy will try to provision a certificate for this name. As an\n\t// outer SNI, it is never used for application data (HTTPS, etc.), but it\n\t// is necessary for enabling clients to connect securely in some cases.","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/ech.go#L670-L706","documentation":"Final of three storage writes for a new ECH config: meta.json (creation timestamp) under ech/configs/<configID>/. A failure leaves the config ID half-populated (key.bin and config.bin present, meta.json missing). The config-creation routine returns the error, so ECH is not enabled for this start; the stranded files are ignored on later runs except that the ID is considered taken.","triggerScenarios":"ctx.Storage().Store for meta.json fails after two prior successful writes — transient storage errors, disk-full occurring mid-sequence, remote storage timeout.","commonSituations":"Same class as the other ECH store failures: intermittent remote storage, quota exhaustion between writes, container filesystem issues.","solutions":["Inspect the wrapped storage error (it names the real cause) and fix the backend.","Restart Caddy to retry provisioning with a new config ID.","Clean up partial ech/configs/<id> directories missing meta.json to reclaim IDs if the 256-ID space ever fills.","Stabilize the storage layer (disk space, network, credentials) before re-enabling ECH."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"storing ECH config metadata\") {\n    // final write failed; retry provisioning after storage repair\n}","preventionTips":["Ensure storage consistency (disk space, network) across all three writes.","Clean partial ECH config dirs during maintenance to reclaim the 256-ID space."],"tags":["tls","ech","storage","partial-write"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}