fatedier/frp · error · configmgmt.ErrInvalidArgument
invalid argument: invalid visitor config: type is required
Error message
invalid argument: invalid visitor config: type is required
What it means
Returned by UpdateStoreVisitor when the decoded body yields a nil VisitorConfigurer. The controller maps the body's "type" discriminator to a concrete visitor configurer; a body with no valid type (missing, misspelled, wrong case, or unparseable payload) decodes to nil and fails this guard with 'type is required' (ErrInvalidArgument → HTTP 400).
Source
Thrown at client/config_manager.go:306
}
return err
}
return nil
})
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
})View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Set "type" to a valid visitor type: stcp, sudp, or xtcp (lowercase) in the JSON body.
- Verify Content-Type: application/json and that the payload parses as JSON.
- Round-trip from GET /api/store/visitors/{name} and edit from the returned shape.
Example fix
# before
curl -XPUT .../api/store/visitors/db-v -d '{"name":"db-v","serverName":"db",...}'
# 400 invalid visitor config: type is required
# after
curl -XPUT .../api/store/visitors/db-v -d '{"type":"stcp","name":"db-v","serverName":"db",...}' Defensive patterns
Strategy: validation
Validate before calling
// Require a valid visitor type before PUT.
visitorTypes := map[string]bool{"stcp": true, "sudp": true, "xtcp": true}
if !visitorTypes[body.Type] {
return fmt.Errorf("missing or invalid visitor type %q (want stcp/sudp/xtcp)", body.Type)
} Try / catch
if _, err := mgr.UpdateStoreVisitor(name, cfg); err != nil {
if errors.Is(err, configmgmt.ErrInvalidArgument) && strings.Contains(err.Error(), "type is required") {
// decoding yielded nil configurer: set type to stcp/sudp/xtcp and resend as JSON
}
} Prevention
- Remember visitors support only stcp/sudp/xtcp — never proxy types like tcp/http
- Round-trip from GET /api/store/visitors/{name} before editing
When it happens
Trigger: PUT /api/store/visitors/{name} with a body missing "type"; type set to a proxy type like "tcp" instead of a visitor type (stcp/sudp/xtcp); sending YAML or form data where JSON is expected.
Common situations: Copying a proxy definition as the base for a visitor and forgetting to change type; case errors ("STCP"); clients defaulting Content-Type incorrectly so the strict decoder rejects the body.
Related errors
- invalid argument: invalid proxy config: type is required
- invalid argument: visitor name is required
- invalid argument: proxy name is required
- invalid argument: validation error: %v
- invalid argument: proxy name in URL must match name in body
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/fcc27ba3972d9f56.
Report an issue: GitHub.