caddyserver/caddy · error

[%s] invalid array index '%s': %v

Error message

[%s] invalid array index '%s': %v

What it means

Thrown during config traversal when the second-to-last path part resolves to an array (the destination), and the final part is used as an array index, but it is not a valid integer. parseCanonicalArrayIndex rejects empty strings and non-numeric values. Example: GET /config/apps/http/servers/myserver/routes/first.

Source

Thrown at admin.go:1216

		parts = parts[:len(parts)-1]
	}

	var ptr any = rawCfg

traverseLoop:
	for i, part := range parts {
		switch v := ptr.(type) {
		case map[string]any:
			// if the next part enters a slice, and the slice is our destination,
			// handle it specially (because appending to the slice copies the slice
			// header, which does not replace the original one like we want)
			if arr, ok := v[part].([]any); ok && i == len(parts)-2 {
				var idx int
				if method != http.MethodPost {
					idxStr := parts[len(parts)-1]
					idx, err = parseCanonicalArrayIndex(idxStr)
					if err != nil {
						return fmt.Errorf("[%s] invalid array index '%s': %v",
							path, idxStr, err)
					}

					if idx < 0 || (method != http.MethodPut && idx >= len(arr)) || idx > len(arr) {
						return fmt.Errorf("[%s] array index out of bounds: %s", path, idxStr)
					}
				}

				switch method {
				case http.MethodGet:
					err = enc.Encode(arr[idx])
					if err != nil {
						return fmt.Errorf("encoding config: %v", err)
					}
				case http.MethodPost:
					if ellipses {
						valArray, ok := val.([]any)
						if !ok {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use a numeric zero-based index, e.g. .../routes/0
  2. Check the shape first: GET the parent array (e.g. .../routes) and count elements
  3. Prefer /id/<@id> addressing if you want name-based access to an array element

Example fix

# before
curl http://localhost:2019/config/apps/http/servers/myserver/routes/first
# after
curl http://localhost:2019/config/apps/http/servers/myserver/routes/0
Defensive patterns

Strategy: validation

Validate before calling

last = path.rstrip('/').split('/')[-1]
assert last.lstrip('-').isdigit() and last != '', f'last segment must be an integer index, got {last!r}'

Prevention

When it happens

Trigger: GET/PUT/PATCH/DELETE on /config/... where the last segment addresses an element of an array but is not a number, e.g. .../routes/new or .../routes/ (empty). POST is exempt because it appends.

Common situations: Assuming routes/keys are named maps when they are arrays in Caddy's JSON schema; using a human name or @id where a numeric index is required; trailing slash producing an empty final segment.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/66d5c8635f3dd05e. Report an issue: GitHub.