caddyserver/caddy · error

invalid traversal path at: %s

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.

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)

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.