caddyserver/caddy · error

[/ ] array index out of bounds

Error message

[/%s] array index out of bounds: %s

What it means

Thrown while traversing an intermediate array when the numeric index is negative or >= the array length. Unlike the final-segment case, there is no PUT append allowance here: intermediate hops must land on an existing element. The message includes the traversed prefix and the bad index.

Solutions

  1. GET the array at the failing prefix to see its actual length, then use a valid 0..len-1 index
  2. Recompute indexes after any DELETE that shifts subsequent elements
  3. Generate paths programmatically from the live config instead of hardcoding indexes

Example fix

# before (only 1 route exists)
curl http://localhost:2019/config/apps/http/servers/myserver/routes/2/handle/0
# after
curl http://localhost:2019/config/apps/http/servers/myserver/routes/0/handle/0
Defensive patterns

Strategy: validation

Validate before calling

prefix="apps/http/servers/myserver/routes"
n=$(curl -s "http://localhost:2019/config/$prefix" | jq 'length')
assert 0 <= idx < n, f'index {idx} out of range for array of {n}'

Try / catch

On out-of-bounds during traversal, GET the prefix from the error message, recompute a valid index, and retry the full path once; persistent failure means your path model is wrong.

Prevention

When it happens

Trigger: GET /config/apps/http/servers/myserver/routes/3/... when the routes array has fewer elements; any path that indexes past the end of an intermediate array such as match sets or handler lists.

Common situations: 0-based vs 1-based confusion; stale indexes after elements were deleted or the config reloaded with fewer entries; paths generated from a template with an unfilled or default index.

Related errors


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

Appendix: source

Thrown at admin.go:1325

				}
			} else {
				// if we are "PUTting" a new resource, the key(s) in its path
				// might not exist yet; that's OK but we need to make them as
				// we go, while we still have a pointer from the level above
				if v[part] == nil && method == http.MethodPut {
					v[part] = make(map[string]any)
				}
				ptr = v[part]
			}

		case []any:
			partInt, err := parseCanonicalArrayIndex(part)
			if err != nil {
				return fmt.Errorf("[/%s] invalid array index '%s': %v",
					strings.Join(parts[:i+1], "/"), part, err)
			}
			if partInt < 0 || partInt >= len(v) {
				return fmt.Errorf("[/%s] array index out of bounds: %s",
					strings.Join(parts[:i+1], "/"), part)
			}
			ptr = v[partInt]

		default:
			return fmt.Errorf("invalid traversal path at: %s", strings.Join(parts[:i+1], "/"))
		}
	}

	return nil
}

// RemoveMetaFields removes meta fields like "@id" from a JSON message
// by using a simple regular expression. (An alternate way to do this
// would be to delete them from the raw, map[string]any
// representation as they are indexed, then iterate the index we made
// and add them back after encoding as JSON, but this is simpler.)
func RemoveMetaFields(rawJSON []byte) []byte {

View on GitHub (pinned to 50e54ee279)