fatedier/frp · error

decode visitor at index %d: %w

Error message

decode visitor at index %d: %w

What it means

DecodeClientConfigJSON iterates raw.Visitors and decodes each entry with DecodeVisitorConfigurerJSON; any per-entry failure is wrapped with the entry's index as 'decode visitor at index %d: %w'. The %w chain preserves the root cause (unknown visitor type, field mismatch, plugin error) while the index identifies which visitors[] element failed.

Source

Thrown at pkg/config/v1/decode.go:186

		Proxies:            make([]TypedProxyConfig, 0, len(raw.Proxies)),
		Visitors:           make([]TypedVisitorConfig, 0, len(raw.Visitors)),
	}

	for i, proxyData := range raw.Proxies {
		proxyCfg, err := DecodeProxyConfigurerJSON(proxyData, options)
		if err != nil {
			return ClientConfig{}, fmt.Errorf("decode proxy at index %d: %w", i, err)
		}
		cfg.Proxies = append(cfg.Proxies, TypedProxyConfig{
			Type:            proxyCfg.GetBaseConfig().Type,
			ProxyConfigurer: proxyCfg,
		})
	}

	for i, visitorData := range raw.Visitors {
		visitorCfg, err := DecodeVisitorConfigurerJSON(visitorData, options)
		if err != nil {
			return ClientConfig{}, fmt.Errorf("decode visitor at index %d: %w", i, err)
		}
		cfg.Visitors = append(cfg.Visitors, TypedVisitorConfig{
			Type:              visitorCfg.GetBaseConfig().Type,
			VisitorConfigurer: visitorCfg,
		})
	}

	return cfg, nil
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Use the reported index to locate the exact visitors[] element (0-based) in the config.
  2. Fix that element per the wrapped cause error (type value, field type/name, or plugin block).
  3. Reload and address any subsequent indexed errors.

Example fix

# error: decode visitor at index 0: unknown visitor type: tcp
# before
[[visitors]]
type = "tcp"
name = "v"

# after
[[visitors]]
type = "stcp"
name = "v"
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: validate each visitors[] entry individually.
func lintVisitors(b []byte, opts v1.DecodeOptions) error {
	var probe struct {
		Visitors []json.RawMessage `json:"visitors"`
	}
	if err := json.Unmarshal(b, &probe); err != nil {
		return err
	}
	for i, raw := range probe.Visitors {
		if _, err := v1.DecodeVisitorConfigurerJSON(raw, opts); err != nil {
			return fmt.Errorf("visitors[%d]: %w", i, err)
		}
	}
	return nil
}

Try / catch

if _, err := v1.DecodeClientConfigJSON(b, opts); err != nil {
	if strings.Contains(err.Error(), "decode visitor at index") {
		// index is 0-based into visitors[]; unwrap for the root cause
	}
	return err
}

Prevention

When it happens

Trigger: Loading a client config where the Nth element of visitors[] (0-based) has an unsupported visitor type, a wrong-typed field, or an invalid plugin block. Reached whenever DecodeClientConfigJSON processes the visitors array.

Common situations: Visitor entries copied from proxy examples; stcp/xtcp visitors with field renames after upgrades; one malformed element among many generated visitors.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/af7a25d81ed91698. Report an issue: GitHub.