fatedier/frp · error

failed to decode visitor at index %d: %w

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.

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)

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.