{"record":{"id":"e39ff00eddb9fce8","repo":"caddyserver/caddy","slug":"must-be-exactly-one-listener-address-cannot-liste","errorCode":null,"errorMessage":"must be exactly one listener address; cannot listen on: %s","messagePattern":"must be exactly one listener address; cannot listen on: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"admin.go","lineNumber":1404,"sourceCode":"\treturn e.Message\n}\n\n// parseAdminListenAddr extracts a singular listen address from either addr\n// or defaultAddr, returning the network and the address of the listener.\nfunc parseAdminListenAddr(addr string, defaultAddr string) (NetworkAddress, error) {\n\tinput, err := NewReplacer().ReplaceOrErr(addr, true, true)\n\tif err != nil {\n\t\treturn NetworkAddress{}, fmt.Errorf(\"replacing listen address: %v\", err)\n\t}\n\tif input == \"\" {\n\t\tinput = defaultAddr\n\t}\n\tlistenAddr, err := ParseNetworkAddress(input)\n\tif err != nil {\n\t\treturn NetworkAddress{}, fmt.Errorf(\"parsing listener address: %v\", err)\n\t}\n\tif listenAddr.PortRangeSize() != 1 {\n\t\treturn NetworkAddress{}, fmt.Errorf(\"must be exactly one listener address; cannot listen on: %s\", listenAddr)\n\t}\n\treturn listenAddr, nil\n}\n\n// decodeBase64DERCert base64-decodes, then DER-decodes, certStr.\nfunc decodeBase64DERCert(certStr string) (*x509.Certificate, error) {\n\tderBytes, err := base64.StdEncoding.DecodeString(certStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn x509.ParseCertificate(derBytes)\n}\n\ntype loggableURLArray []*url.URL\n\nfunc (ua loggableURLArray) MarshalLogArray(enc zapcore.ArrayEncoder) error {\n\tif ua == nil {\n\t\treturn nil","sourceCodeStart":1386,"sourceCodeEnd":1422,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/admin.go#L1386-L1422","documentation":"The admin API binds exactly one listener, so parseAdminListenAddr rejects any address whose PortRangeSize() != 1. Addresses like 'localhost:2020-2025' or a hostname that expands to multiple ports parse fine but describe a port range, and this error names the offending address. It fires before any socket is opened.","triggerScenarios":"Setting 'admin localhost:2020-2025' in a Caddyfile or \"admin\":{\"listen\":\"localhost:2020-2025\"} in JSON; any admin listen value with a port range or wildcard port expansion that yields more than one port.","commonSituations":"Copy-pasting site-listen patterns (port ranges are valid for HTTP apps) into the admin block; attempting load-balanced admin across a port range; misunderstanding that the admin endpoint is a singular, unique listener.","solutions":["Change the admin listen address to a single port: 'admin localhost:2019'.","If you need multiple admin surfaces, run separate Caddy instances rather than a port range.","Double-check for accidental dashes or commas in the port field."],"exampleFix":"// before\nadmin localhost:2020-2025\n\n// after\nadmin localhost:2019","handlingStrategy":"validation","validationCode":"na, err := caddy.ParseNetworkAddress(adminAddr)\nif err != nil {\n    return err\n}\nif na.PortRangeSize() != 1 {\n    return fmt.Errorf(\"admin needs exactly one port, got %d in %s\", na.PortRangeSize(), adminAddr)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never carry site-style port ranges into the admin directive.","Treat the admin endpoint as a single singleton listener in architecture reviews."],"tags":["caddy","admin-api","config","ports","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}