{"record":{"id":"578dad014ff7a1f7","repo":"caddyserver/caddy","slug":"storing-ech-private-key-v","errorCode":null,"errorMessage":"storing ECH private key: %v","messagePattern":"storing ECH private key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/ech.go","lineNumber":682,"sourceCode":"\tif err != nil {\n\t\treturn echConfig{}, fmt.Errorf(\"marshaling ECH private key: %v\", err)\n\t}\n\techConfigBytes, err := echCfg.MarshalBinary()\n\tif err != nil {\n\t\treturn echConfig{}, fmt.Errorf(\"marshaling ECH config: %v\", err)\n\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.","sourceCodeStart":664,"sourceCodeEnd":700,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/ech.go#L664-L700","documentation":"Caddy persists each new ECH config as three objects under ech/configs/<configID>/ in the configured storage: key.bin (HPKE private key), config.bin, and meta.json. This is the first Store call; it fails when the storage backend rejects the write — permissions, disk full, read-only filesystem, or a remote storage provider error (auth, network, quota). Because it is the first of three writes, a failure here leaves no partial config behind.","triggerScenarios":"ECH provisioning triggered (first start with ech on, or all existing ECH configs expired/rotated) while ctx.Storage().Store fails: read-only volume, exhausted disk, s3/restic/redis storage auth failure or network outage.","commonSituations":"Running Caddy in a container with a read-only or unmounted data volume; file_storage directory owned by root while Caddy drops privileges; disk-full nodes; distributed storage briefly unavailable during certificate/ECH provisioning.","solutions":["Verify write access of the storage path/backend: have Caddy's user create a file under the storage prefix (default data dir, or your storage module's location).","Free disk space or fix quota on the storage volume.","For remote storage modules, check credentials and connectivity (the module usually logs a more specific error just before).","Restart Caddy after fixing storage; provisioning will retry and find a free config ID."],"exampleFix":"// before: storage dir not writable\n$ ls -ld /var/lib/caddy\n drwx------ root root\n\n// after\n$ chown -R caddy:caddy /var/lib/caddy","handlingStrategy":"retry","validationCode":"// Pre-flight: ensure the storage prefix is writable.\nfunc probeECHStorageWrite(ctx context.Context, stor caddy.Storage) error {\n    return stor.Store(ctx, \"ech/configs/.probe\", []byte(\"ok\"))\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"storing ECH private key\") {\n    // storage-level failure: surface wrapped cause, fix backend, then restart to retry\n    log.Printf(\"ECH store failed, check storage: %v\", err)\n}","preventionTips":["Run the Caddy service under a user that owns the data directory.","Monitor disk space and remote storage auth expiry.","No partial state is left by this specific failure; safe to retry after fixing storage."],"tags":["tls","ech","storage","permissions"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}