ory/kratos · error

no oidc provider was set

Error message

no oidc provider was set

What it means

During the settings flow, Link unmarshals the identity's stored oidc credentials config and requires exactly one provider to link. If the credentials JSON holds zero or more than one OIDC providers, this error is returned because the linking operation cannot disambiguate which provider to use.

Solutions

  1. Ensure the identity actually has exactly one linked OIDC provider before attempting to link more credentials
  2. Inspect the identity via GET /admin/identities/{id} and check credentials.oidc.config.providers length
  3. If multiple providers are linked, link via the specific provider by re-authenticating through the OIDC flow rather than the settings link endpoint
Defensive patterns

Strategy: type-guard

Validate before calling

var cfg identity.CredentialsOIDC; json.Unmarshal(creds.Config, &cfg); if len(cfg.Providers) != 1 { /* skip linking */ }

Type guard

func hasExactlyOneOIDCProvider(c identity.CredentialsOIDC) bool { return len(c.Providers) == 1 }

Try / catch

if err := s.linkCredentials(...); err != nil && strings.Contains(err.Error(), "no oidc provider was set") { // prompt user to link via OIDC login instead }

Prevention

When it happens

Trigger: Calling the settings-flow link endpoint for the oidc credentials type when the identity's credentials_config.providers array is empty or contains multiple entries.

Common situations: Identity was created without any linked OIDC provider; corrupt or manually edited credentials metadata; a legacy identity that accumulated several linked providers.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/33875673b18c223a. Report an issue: GitHub.

Appendix: source

Thrown at selfservice/strategy/oidc/strategy_settings.go:634

	if ctxUpdate.Flow != nil {
		ctxUpdate.Flow.UI.ResetMessages()
		ctxUpdate.Flow.UI.SetCSRF(s.d.GenerateCSRFToken(r))
	}

	return err
}

func (s *Strategy) Link(ctx context.Context, i *identity.Identity, credentialsConfig sqlxx.JSONRawMessage) (err error) {
	ctx, span := s.d.Tracer(ctx).Tracer().Start(ctx, "selfservice.strategy.oidc.Strategy.Link")
	defer otelx.End(span, &err)

	var credentialsOIDCConfig identity.CredentialsOIDC
	if err := json.Unmarshal(credentialsConfig, &credentialsOIDCConfig); err != nil {
		return err
	}
	if len(credentialsOIDCConfig.Providers) != 1 {
		return errors.New("no oidc provider was set")
	}
	credentialsOIDCProvider := credentialsOIDCConfig.Providers[0]

	if err := s.linkCredentials(
		ctx,
		i,
		// The tokens in this credential are coming from the existing identity. Hence, the values are already encrypted.
		credentialsOIDCProvider.GetTokens(),
		credentialsOIDCProvider.Provider,
		credentialsOIDCProvider.Subject,
		credentialsOIDCProvider.Organization,
	); err != nil {
		return err
	}

	if err := s.d.IdentityManager().Update(ctx, i, identity.ManagerAllowWriteProtectedTraits); err != nil {
		return err
	}

View on GitHub (pinned to b86338da04)