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
- Make the URL name and the body name identical (pick the target name, set it in both places)
- If you intend to rename, delete the old visitor (DELETE /api/config/visitors/{old}) and create a new one with the new name
- 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
- Derive URL path segment and body name from the same variable in client code
- For renames, use delete-then-create instead of update
- Assert on HTTP 400 responses as a config bug in your automation, not a runtime condition
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
- store disabled: store API is disabled
- invalid proxy config
- invalid visitor config
- invalid argument: proxy name is required
- invalid argument: validation error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/22ebf5c52bd6cf20.
Report an issue: GitHub.