{"record":{"id":"3410c68cade66443","repo":"caddyserver/caddy","slug":"unmarshaling-admin-listener-address-from-config","errorCode":null,"errorMessage":"unmarshaling admin listener address from config: %v","messagePattern":"unmarshaling admin listener address from config: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/commandfuncs.go","lineNumber":894,"sourceCode":"\t\tif len(loadedConfig) == 0 {\n\t\t\t// get the config in caddy's native format\n\t\t\tloadedConfig, loadedConfigFile, _, err = LoadConfig(configFile, configAdapter)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tif loadedConfigFile == \"\" {\n\t\t\t\treturn \"\", fmt.Errorf(\"no config file to load; either use --config flag or ensure Caddyfile exists in current directory\")\n\t\t\t}\n\t\t}\n\n\t\t// get the address of the admin listener from the config\n\t\tif len(loadedConfig) > 0 {\n\t\t\tvar tmpStruct struct {\n\t\t\t\tAdmin caddy.AdminConfig `json:\"admin\"`\n\t\t\t}\n\t\t\terr := json.Unmarshal(loadedConfig, &tmpStruct)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"unmarshaling admin listener address from config: %v\", err)\n\t\t\t}\n\t\t\tif tmpStruct.Admin.Listen != \"\" {\n\t\t\t\treturn tmpStruct.Admin.Listen, nil\n\t\t\t}\n\t\t}\n\t}\n\n\t// Fallback to the default listen address otherwise\n\treturn caddy.DefaultAdminListen, nil\n}\n\n// configFileWithRespectToDefault returns the filename to use for loading the config, based\n// on whether a config file is already specified and a supported default config file exists.\nfunc configFileWithRespectToDefault(logger *zap.Logger, configFile string) (string, error) {\n\tconst defaultCaddyfile = \"Caddyfile\"\n\n\t// if no input file was specified, try a default Caddyfile if the Caddyfile adapter is plugged in\n\tif configFile == \"\" && caddyconfig.GetAdapter(\"caddyfile\") != nil {","sourceCodeStart":876,"sourceCodeEnd":912,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/cmd/commandfuncs.go#L876-L912","documentation":"DetermineAdminAPIAddress unmarshals only the top-level {\"admin\": {...}} field of the loaded config to find admin.listen; if those bytes are not valid JSON this wrap of the json.Unmarshal error is returned. With a --adapter this should not happen (the adapter output is JSON), so it nearly always means a raw JSON file is corrupt.","triggerScenarios":"Passing --config pointing at a hand-edited JSON file with a typo; a truncated file (disk full, interrupted write); a JSON file with a BOM or CRLF issues that break parsing at the admin key level.","commonSituations":"Scripts that generate the JSON config emitting invalid JSON; editing the config in place while a reload tool concurrently reads it.","solutions":["Validate the JSON: jq . /path/to/config.json or caddy validate --config <file>","Write config files atomically (write temp file, then rename) so readers never see partial content","If using a Caddyfile or other format, pass the matching --adapter so it is converted to JSON first"],"exampleFix":"# before\ncaddy reload --config caddy.json   # file has trailing comma\n\n# after\njq . caddy.json >/dev/null && caddy reload --config caddy.json","handlingStrategy":"validation","validationCode":"// Pre-parse to catch corrupt JSON before the CLI does:\nvar probe struct{ Admin caddy.AdminConfig `json:\"admin\"` }\nif err := json.Unmarshal(raw, &probe); err != nil { return fmt.Errorf(\"config JSON invalid: %w\", err) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint generated JSON with jq before handing it to caddy","Write configs atomically (temp file + rename)","Pair non-JSON configs with the right --adapter"],"tags":["cli","configuration","json","parsing"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}