caddyserver/caddy · error

no traversable path

Error message

no traversable path

What it means

Returned by unsyncedConfigAccess when the config path, after trimming leading and trailing slashes, is the empty string. This guards the traversal splitter before it starts; with an empty path there is nothing to walk in the config tree. It indicates the caller asked for the root in a way the handler does not accept.

Source

Thrown at admin.go:1180

	// if there is a request body, decode it into the
	// variable that will be set in the config according
	// to method and path
	if len(body) > 0 {
		err = json.Unmarshal(body, &val)
		if err != nil {
			if jsonErr, ok := err.(*json.SyntaxError); ok {
				return fmt.Errorf("decoding request body: %w, at offset %d", jsonErr, jsonErr.Offset)
			}
			return fmt.Errorf("decoding request body: %w", err)
		}
	}

	enc := json.NewEncoder(out)

	cleanPath := strings.Trim(path, "/")
	if cleanPath == "" {
		return fmt.Errorf("no traversable path")
	}

	parts := strings.Split(cleanPath, "/")
	if len(parts) == 0 {
		return fmt.Errorf("path missing")
	}

	// A path that ends with "..." implies:
	// 1) the part before it is an array
	// 2) the payload is an array
	// and means that the user wants to expand the elements
	// in the payload array and append each one into the
	// destination array, like so:
	//     array = append(array, elems...)
	// This special case is handled below.
	ellipses := parts[len(parts)-1] == "..."
	if ellipses {
		parts = parts[:len(parts)-1]

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Pass a real config sub-path such as apps/http/servers instead of an empty or slash-only string
  2. If you want the whole config, GET /config/ rather than constructing an empty-path request

Example fix

// before
err := unsyncedConfigAccess(http.MethodGet, "/", nil, &buf)
// after
err := unsyncedConfigAccess(http.MethodGet, "apps/http/servers", nil, &buf)
Defensive patterns

Strategy: validation

Validate before calling

clean = path.strip('/')
assert clean, 'path must address a config subtree'
unsyncedConfigAccess(method, clean, body, out)  # Go, internal callers

Prevention

When it happens

Trigger: Passing a path of "/", "//", or "" to the internal config access (as the admin API does when a request targets only the base, e.g. via certain /load or internal dispatch forms).

Common situations: Code calling unsyncedConfigAccess directly with a degenerate path; clients hitting the wrong endpoint so the leftover path is empty. Rarely seen by pure HTTP users because the admin router routes '/' to different handlers.

Related errors


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