{"record":{"id":"babda5b00dd37f64","repo":"caddyserver/caddy","slug":"final-element-is-not-an-array","errorCode":null,"errorMessage":"final element is not an array","messagePattern":"final element is not an array","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"admin.go","lineNumber":1235,"sourceCode":"\t\t\t\t\t\t\tpath, idxStr, err)\n\t\t\t\t\t}\n\n\t\t\t\t\tif idx < 0 || (method != http.MethodPut && idx >= len(arr)) || idx > len(arr) {\n\t\t\t\t\t\treturn fmt.Errorf(\"[%s] array index out of bounds: %s\", path, idxStr)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch method {\n\t\t\t\tcase http.MethodGet:\n\t\t\t\t\terr = enc.Encode(arr[idx])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"encoding config: %v\", err)\n\t\t\t\t\t}\n\t\t\t\tcase http.MethodPost:\n\t\t\t\t\tif ellipses {\n\t\t\t\t\t\tvalArray, ok := val.([]any)\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn fmt.Errorf(\"final element is not an array\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tv[part] = append(arr, valArray...)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tv[part] = append(arr, val)\n\t\t\t\t\t}\n\t\t\t\tcase http.MethodPut:\n\t\t\t\t\t// avoid creation of new slice and a second copy (see\n\t\t\t\t\t// https://github.com/golang/go/wiki/SliceTricks#insert)\n\t\t\t\t\tarr = append(arr, nil)\n\t\t\t\t\tcopy(arr[idx+1:], arr[idx:])\n\t\t\t\t\tarr[idx] = val\n\t\t\t\t\tv[part] = arr\n\t\t\t\tcase http.MethodPatch:\n\t\t\t\t\tarr[idx] = val\n\t\t\t\tcase http.MethodDelete:\n\t\t\t\t\tv[part] = append(arr[:idx], arr[idx+1:]...)\n\t\t\t\tdefault:\n\t\t\t\t\treturn fmt.Errorf(\"unrecognized method %s\", method)","sourceCodeStart":1217,"sourceCodeEnd":1253,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/admin.go#L1217-L1253","documentation":"Thrown when POST targets an array destination with the '...' ellipsis form (path ends in '...') but the request body is not a JSON array. The ellipsis contract is: the parent is an array, the final path part is '...', and the payload must itself be an array whose elements are appended one by one.","triggerScenarios":"POST /config/apps/http/servers/myserver/routes/... with body '{\"handle\":[...]}' (an object) or '\"x\"' (a string). Only a JSON array payload is accepted, e.g. '[{...},{...}]'.","commonSituations":"Misreading the ellipsis feature and sending a single object to be wrapped; sending an array nested one level too deep or too shallow.","solutions":["Wrap the payload in a JSON array: POST '[ { \"handle\": ... } ]' to .../routes/...","Or drop the ellipsis and POST the single object directly to .../routes to append one element"],"exampleFix":"# before\ncurl -X POST http://localhost:2019/config/apps/http/servers/myserver/routes/... \\\n  -d '{\"handle\":[{\"handler\":\"static_response\",\"body\":\"hi\"}]}'\n# after\ncurl -X POST http://localhost:2019/config/apps/http/servers/myserver/routes/... \\\n  -d '[{\"match\":[{\"host\":[\"a.example\"]}],\"handle\":[{\"handler\":\"static_response\",\"body\":\"hi\"}]}]'","handlingStrategy":"type-guard","validationCode":"import json\nval = json.loads(body)\nassert isinstance(val, list), 'ellipsis (...) POST requires an array payload'","typeGuard":"func isJSONArray(body []byte) bool {\n    var v any\n    if json.Unmarshal(body, &v) != nil {\n        return false\n    }\n    _, ok := v.([]any)\n    return ok\n}","tryCatchPattern":null,"preventionTips":["Pair '...' paths with array payloads by convention in your client library","Single-element append: POST the object without the ellipsis instead"],"tags":["caddy","admin-api","json","config","bad-request"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}