{"record":{"id":"8e89e382f221495c","repo":"caddyserver/caddy","slug":"depleted-attempts-to-find-an-available-config-id","errorCode":null,"errorMessage":"depleted attempts to find an available config_id","messagePattern":"depleted attempts to find an available config_id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/ech.go","lineNumber":1143,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t\ttried[num] = true\n\n\t\t// check to see if any of the subkeys use this config ID\n\t\tnumStr := strconv.Itoa(int(num))\n\t\ttrialPath := path.Join(echConfigsKey, numStr)\n\t\tif ctx.Storage().Exists(ctx, trialPath) {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn num, nil\n\t}\n\n\tif err := ctx.Err(); err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn 0, fmt.Errorf(\"depleted attempts to find an available config_id\")\n}\n\n// ECHPublisher is an interface for publishing ECHConfigList values\n// so that they can be used by clients.\ntype ECHPublisher interface {\n\t// Returns a key that is unique to this publisher and its configuration.\n\t// A publisher's ID combined with its config is a valid key.\n\t// It is used to prevent duplicating publications.\n\tPublisherKey() string\n\n\t// Publishes the ECH config list (as binary) for the given innerNames. Some\n\t// publishers may not need a list of inner/protected names, and can ignore the\n\t// argument; most, however, will want to use it to know which inner names are\n\t// to be associated with the given ECH config list.\n\t//\n\t// Implementations should return an error of type PublishECHConfigListErrors\n\t// when relevant to key errors to their associated innerName, but should never\n\t// return a non-nil PublishECHConfigListErrors when its length is 0.","sourceCodeStart":1125,"sourceCodeEnd":1161,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/ech.go#L1125-L1161","documentation":"ECH config IDs are a single byte (0-255). newECHConfigID does rejection sampling: up to 256 random draws, skipping IDs whose ech/configs/<id> path already Exists in storage. If every distinct ID was either tried-and-taken (or context cancelled, which is returned separately), allocation gives up with this error. In practice it means storage holds entries for (nearly) all 256 IDs — usually accumulated stale/orphaned configs from rotations or partial writes.","triggerScenarios":"The ech/configs/ prefix in storage contains subdirectories for all 256 possible numeric IDs — e.g. after many ECH rotations without cleanup, or repeated failed provisioning runs that each stranded partial entries (key.bin written but later step failed).","commonSituations":"Long-running Caddy with ECH on and rotation creating new configs each interval; storage littered by crash-looping during provisioning; copying a cluttered storage between environments.","solutions":["List ech/configs/ in storage and delete stale entries (those with old Created timestamps in meta.json, or directories missing config.bin/meta.json).","If rotation naturally fills the space, prune expired ECH configs as part of maintenance.","Verify no process is repeatedly creating partial ECH configs (check for the per-write store errors) and fix that root cause.","Restart Caddy after cleanup so provisioning retries ID allocation."],"exampleFix":"# before: all 256 IDs occupied\n$ ls ~/.local/share/caddy/ech/configs | wc -l\n256\n\n# after: remove stale/partial entries (keep recent ones)\n$ find ~/.local/share/caddy/ech/configs -name meta.json -mtime +30 \\\n    -exec dirname {} \\; | xargs rm -rf","handlingStrategy":"validation","validationCode":"// Maintenance check: count occupied ECH config IDs and prune stale ones.\nfunc echIDUsage(ctx context.Context, stor caddy.Storage) (int, error) {\n    entries, err := stor.List(ctx, \"ech/configs\", false)\n    if err != nil {\n        return 0, err\n    }\n    return len(entries), nil\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"depleted attempts to find an available config_id\") {\n    // storage has (nearly) all 256 IDs taken: prune ech/configs/* and restart\n}","preventionTips":["Periodically delete old ECH configs (rotation accumulates entries; ID space is 256).","Clean partial ech/configs/<id> dirs missing config.bin or meta.json after storage incidents.","Alert when occupied ECH IDs exceed ~200 so cleanup happens before exhaustion."],"tags":["tls","ech","storage","capacity","rotation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}