fatedier/frp · error · configmgmt.ErrInvalidArgument

invalid argument: visitor name in URL must match name in bod

Error message

invalid argument: visitor name in URL must match name in body

What it means

Thrown by frpc's config store API (UpdateStoreVisitor) when the visitor name in the URL path does not equal the name inside the request body's visitor config. The client enforces name consistency so an update request cannot silently rename a visitor. Maps to HTTP 400 via configmgmt.ErrInvalidArgument in client/http/controller.go.

Source

Thrown at client/config_manager.go:310

	})
	if err != nil {
		return nil, err
	}

	log.Infof("store: created visitor %q", name)
	return persisted, nil
}

func (m *serviceConfigManager) UpdateStoreVisitor(name string, cfg v1.VisitorConfigurer) (v1.VisitorConfigurer, error) {
	if name == "" {
		return nil, fmt.Errorf("%w: visitor name is required", configmgmt.ErrInvalidArgument)
	}
	if cfg == nil {
		return nil, fmt.Errorf("%w: invalid visitor config: type is required", configmgmt.ErrInvalidArgument)
	}
	bodyName := cfg.GetBaseConfig().Name
	if bodyName != name {
		return nil, fmt.Errorf("%w: visitor name in URL must match name in body", configmgmt.ErrInvalidArgument)
	}
	if err := m.validateStoreVisitorConfigurer(cfg); err != nil {
		return nil, fmt.Errorf("%w: validation error: %v", configmgmt.ErrInvalidArgument, err)
	}

	persisted, err := m.withStoreVisitorMutationAndReload(name, func(storeSource *source.StoreSource) error {
		if err := storeSource.UpdateVisitor(cfg); err != nil {
			if errors.Is(err, source.ErrNotFound) {
				return fmt.Errorf("%w: %v", configmgmt.ErrNotFound, err)
			}
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Make the URL name and the body name identical (pick the target name, set it in both places)
  2. If you intend to rename, delete the old visitor (DELETE /api/config/visitors/{old}) and create a new one with the new name
  3. In client code, derive both the URL segment and the body name from a single variable

Example fix

// before
name := "ssh-visitor"
url := fmt.Sprintf("/api/config/visitors/%s", name)
body.Name = "ssh-visitor-2" // mismatch

// after
name := "ssh-visitor"
url := fmt.Sprintf("/api/config/visitors/%s", name)
body.Name = name // URL and body always derived from one source
Defensive patterns

Strategy: validation

Validate before calling

func validVisitorUpdateRequest(urlName string, body []byte) error {
	var partial struct{ Name string `json:"name"` }
	if err := json.Unmarshal(body, &partial); err != nil {
		return fmt.Errorf("body is not valid visitor JSON: %w", err)
	}
	if partial.Name == "" || partial.Name != urlName {
		return fmt.Errorf("body name %q must match URL name %q", partial.Name, urlName)
	}
	return nil
}

Prevention

When it happens

Trigger: PUT /api/config/visitors/{name} (UpdateStoreVisitor) where body JSON's name field differs from the {name} path segment, e.g. path says 'ssh-visitor' but body contains "name": "ssh-visitor-2". Only reached when the store is enabled; cfg must be non-nil first.

Common situations: Automation scripts that generate the body from a template but build the URL from a stale variable; renaming a visitor by editing only the body or only the URL; copy-pasting a config and forgetting to sync both places.

Related errors


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