{"record":{"id":"3cf9e21a842841c2","repo":"caddyserver/caddy","slug":"no-traversable-path","errorCode":null,"errorMessage":"no traversable path","messagePattern":"no traversable path","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"admin.go","lineNumber":1180,"sourceCode":"\n\t// if there is a request body, decode it into the\n\t// variable that will be set in the config according\n\t// to method and path\n\tif len(body) > 0 {\n\t\terr = json.Unmarshal(body, &val)\n\t\tif err != nil {\n\t\t\tif jsonErr, ok := err.(*json.SyntaxError); ok {\n\t\t\t\treturn fmt.Errorf(\"decoding request body: %w, at offset %d\", jsonErr, jsonErr.Offset)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"decoding request body: %w\", err)\n\t\t}\n\t}\n\n\tenc := json.NewEncoder(out)\n\n\tcleanPath := strings.Trim(path, \"/\")\n\tif cleanPath == \"\" {\n\t\treturn fmt.Errorf(\"no traversable path\")\n\t}\n\n\tparts := strings.Split(cleanPath, \"/\")\n\tif len(parts) == 0 {\n\t\treturn fmt.Errorf(\"path missing\")\n\t}\n\n\t// A path that ends with \"...\" implies:\n\t// 1) the part before it is an array\n\t// 2) the payload is an array\n\t// and means that the user wants to expand the elements\n\t// in the payload array and append each one into the\n\t// destination array, like so:\n\t//     array = append(array, elems...)\n\t// This special case is handled below.\n\tellipses := parts[len(parts)-1] == \"...\"\n\tif ellipses {\n\t\tparts = parts[:len(parts)-1]","sourceCodeStart":1162,"sourceCodeEnd":1198,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/admin.go#L1162-L1198","documentation":"Returned by unsyncedConfigAccess when the config path, after trimming leading and trailing slashes, is the empty string. This guards the traversal splitter before it starts; with an empty path there is nothing to walk in the config tree. It indicates the caller asked for the root in a way the handler does not accept.","triggerScenarios":"Passing a path of \"/\", \"//\", or \"\" to the internal config access (as the admin API does when a request targets only the base, e.g. via certain /load or internal dispatch forms).","commonSituations":"Code calling unsyncedConfigAccess directly with a degenerate path; clients hitting the wrong endpoint so the leftover path is empty. Rarely seen by pure HTTP users because the admin router routes '/' to different handlers.","solutions":["Pass a real config sub-path such as apps/http/servers instead of an empty or slash-only string","If you want the whole config, GET /config/ rather than constructing an empty-path request"],"exampleFix":"// before\nerr := unsyncedConfigAccess(http.MethodGet, \"/\", nil, &buf)\n// after\nerr := unsyncedConfigAccess(http.MethodGet, \"apps/http/servers\", nil, &buf)","handlingStrategy":"validation","validationCode":"clean = path.strip('/')\nassert clean, 'path must address a config subtree'\nunsyncedConfigAccess(method, clean, body, out)  # Go, internal callers","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Internal callers: reject empty or slash-only paths at the call site","HTTP users: GET /config/ for the whole tree instead of degenerate paths"],"tags":["caddy","admin-api","config","path","internal"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}