fatedier/frp · error

failed to decode visitor at index

Error message

failed to decode visitor at index %d: %w

What it means

The visitor analogue of the proxy decode error: DecodeVisitorConfigurerJSON failed for the i-th element of the visitors array in the store file. Either the "type" discriminator does not match a registered VisitorConfigurer type (stcp, xtcp, sudp) or the payload is structurally incompatible with that type.

Solutions

  1. Check the visitors array element at the reported index in the store file
  2. Confirm "type" is a valid visitor type for the running frp version (stcp, xtcp, sudp)
  3. Upgrade frp if the entry was written by a newer release
  4. Fix the type field or remove the broken entry and retry

Example fix

// before (visitors[0])
{ "type": "stcp", "name": "ssh-visitor" }

// after (if "stcp" unknown to this build, upgrade; or fix e.g. wrong array)
{ "type": "stcp", "name": "ssh-visitor", "serverName": "ssh", "secretKey": "..." }
Defensive patterns

Strategy: validation

Validate before calling

var supportedVisitorTypes = map[string]bool{"stcp": true, "xtcp": true, "sudp": true}

func validateVisitorTypes(path string) error {
	data, _ := os.ReadFile(path)
	var raw struct {
		Visitors []json.RawMessage `json:"visitors"`
	}
	if json.Unmarshal(data, &raw) != nil {
		return nil
	}
	for i, v := range raw.Visitors {
		var t struct{ Type string `json:"type"` }
		if json.Unmarshal(v, &t) != nil || !supportedVisitorTypes[t.Type] {
			return fmt.Errorf("visitors[%d]: unsupported type %q", i, t.Type)
		}
	}
	return nil
}

Prevention

When it happens

Trigger: A visitors entry with a misspelled/missing/unknown "type"; a store written by a newer frp containing visitor types the running build lacks; a payload field with the wrong JSON type for the target struct.

Common situations: Version downgrade after a newer frp wrote the store; hand-edited visitor entries; using a proxy type (e.g. "tcp") in the visitors array, which is not a visitor type.

Understand the failure class

Related errors


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

Appendix: source

Thrown at pkg/config/source/store.go:113

		proxyCfg, err := v1.DecodeProxyConfigurerJSON(proxyData, v1.DecodeOptions{
			DisallowUnknownFields: false,
		})
		if err != nil {
			return fmt.Errorf("failed to decode proxy at index %d: %w", i, err)
		}
		name := proxyCfg.GetBaseConfig().Name
		if name == "" {
			return fmt.Errorf("proxy name cannot be empty")
		}
		s.proxies[name] = proxyCfg
	}

	for i, visitorData := range stored.Visitors {
		visitorCfg, err := v1.DecodeVisitorConfigurerJSON(visitorData, v1.DecodeOptions{
			DisallowUnknownFields: false,
		})
		if err != nil {
			return fmt.Errorf("failed to decode visitor at index %d: %w", i, err)
		}
		name := visitorCfg.GetBaseConfig().Name
		if name == "" {
			return fmt.Errorf("visitor name cannot be empty")
		}
		s.visitors[name] = visitorCfg
	}

	return nil
}

func (s *StoreSource) saveToFileUnlocked() error {
	stored := storeData{
		Proxies:  make([]v1.TypedProxyConfig, 0, len(s.proxies)),
		Visitors: make([]v1.TypedVisitorConfig, 0, len(s.visitors)),
	}

	for _, p := range s.proxies {

View on GitHub (pinned to 6c8a8d0a97)