caddyserver/caddy · error

invalid traversal path at

Error message

invalid traversal path at: %s

What it means

Thrown when a traversal step lands on a config node that is neither a map nor an array — typically a scalar (string, number, bool) — yet the path continues with more segments. The message names the prefix up to and including the offending segment, e.g. 'invalid traversal path at: apps/http/servers/myserver/listen' when something like /0 is appended after listen.

Solutions

  1. GET the exact prefix from the error message to see the node's actual type
  2. End the path at the scalar, e.g. .../listen/0 instead of .../listen/0/x
  3. Cross-check against the JSON config schema docs for leaf node types

Example fix

# before: '0' is a string leaf, cannot descend further
curl http://localhost:2019/config/apps/http/servers/myserver/listen/0/x
# after
curl http://localhost:2019/config/apps/http/servers/myserver/listen/0
Defensive patterns

Strategy: type-guard

Validate before calling

prefix="apps/http/servers/myserver/listen"
type=$(curl -s "http://localhost:2019/config/$prefix" | jq -r 'type')
[ "$type" = "object" -o "$type" = "array" ] && echo "traversable" || echo "leaf — stop here"

Type guard

func traversable(v any) bool {
    switch v.(type) {
    case map[string]any, []any:
        return true
    }
    return false
}

Try / catch

Parse 'invalid traversal path at: <prefix>', GET that prefix to see its JSON type, and truncate or correct the path so it stops at or correctly indexes that node.

Prevention

When it happens

Trigger: GET /config/apps/http/servers/myserver/listen/0 where listen is a JSON array of strings but the scalar case occurs for e.g. .../automatic_https/disable/next, or any path that keeps descending after reaching a string/number/bool leaf such as .../listen/0/extra.

Common situations: Misjudging the config schema depth: treating a leaf scalar as a traversable container; appending extra segments after a full address like admin/timeout/... ; paths built by naive splitting of a desired dotted path.

Related errors


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

Appendix: source

Thrown at admin.go:1331

					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 {
	return idRegexp.ReplaceAllFunc(rawJSON, func(in []byte) []byte {
		// matches with a comma on both sides (when "@id" property is
		// not the first or last in the object) need to keep exactly
		// one comma for correct JSON syntax
		comma := []byte{','}
		if bytes.HasPrefix(in, comma) && bytes.HasSuffix(in, comma) {

View on GitHub (pinned to 50e54ee279)