netbirdio/netbird · error
connector type change not allowed
Error message
connector type change not allowed
What it means
Thrown by Provider.UpdateConnector in the Dex IdP integration (idp/dex/connector.go:100). A stored Dex connector's type is immutable identity: an update may only leave the type field empty (keep current) or set it to the value inferred from the stored connector (inferIdentityProviderType(old.Type, cfg.ID, nil)). Any other type value is rejected before the config overlay is applied.
Source
Thrown at idp/dex/connector.go:100
p.logger.Warn("failed to parse connector", "id", conn.ID, "error", err)
continue
}
result = append(result, cfg)
}
return result, nil
}
// UpdateConnector updates an existing connector in Dex storage.
// It overlays user-mutable config fields (issuer, clientID, clientSecret,
// redirectURI) onto the stored connector config, and updates the connector name
// when cfg.Name is set. Empty fields on cfg leave stored values unchanged, so
// partial updates preserve create-time defaults such as scopes, claimMapping,
// and userIDKey.
func (p *Provider) UpdateConnector(ctx context.Context, cfg *ConnectorConfig) error {
if err := p.storage.UpdateConnector(ctx, cfg.ID, func(old storage.Connector) (storage.Connector, error) {
if cfg.Type != "" && cfg.Type != inferIdentityProviderType(old.Type, cfg.ID, nil) {
return storage.Connector{}, errors.New("connector type change not allowed")
}
configData, err := overlayConnectorConfig(old.Config, cfg)
if err != nil {
return storage.Connector{}, fmt.Errorf("failed to overlay connector config: %w", err)
}
name := cfg.Name
if name == "" {
name = old.Name
}
return storage.Connector{
ID: cfg.ID,
Type: old.Type,
Name: name,
Config: configData,
}, nilView on GitHub (pinned to 93e97f4bf1)
Solutions
- Omit the type field on updates (empty means unchanged).
- To switch provider type, delete the connector and create a new one with the desired type.
- If you believe the type is unchanged, compare against inferIdentityProviderType's expected value for the stored connector and send exactly that.
Example fix
// before
err := provider.UpdateConnector(ctx, &ConnectorConfig{
ID: "netbird", Type: "oidc", // stored connector was created as "google"
})
// after: leave type empty to preserve it
err := provider.UpdateConnector(ctx, &ConnectorConfig{
ID: "netbird", Type: "", // type unchanged; only overlay fields are updated
ClientID: "new-client", ClientSecret: "new-secret",
}) Defensive patterns
Strategy: validation
Validate before calling
// Type is immutable: only send it when it matches the stored connector.
if cfg.Type != "" && cfg.Type != expectedStoredType {
return fmt.Errorf("cannot change connector type %q to %q; recreate the connector instead", expectedStoredType, cfg.Type)
} Try / catch
if err := provider.UpdateConnector(ctx, cfg); err != nil {
if strings.Contains(err.Error(), "connector type change not allowed") {
// recreate with the new type instead of updating
}
return err
} Prevention
- Send partial updates: omit the type field unless you are sure it matches.
- Model connector type as immutable in clients; offer delete + create for type changes.
When it happens
Trigger: Calling UpdateConnector with cfg.Type set to a different connector type than the stored one, e.g. updating a connector created as 'google' with Type: "oidc", or an editor round-tripping a full config object that carries a stale/edited type field.
Common situations: Management UI edit forms that submit the whole object including a type dropdown the user changed; API clients PUTting a full config where the type was normalized differently (case or alias); attempting to 'convert' a connector in place instead of recreating it.
Related errors
- service name is required
- service name exceeds maximum length of 255 characters
- at least one target is required
- private services require at least one access group
- private services cannot enable bearer auth (SSO): NetBird-on
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/badaf2ad573a0ce0.
Report an issue: GitHub.