siyuan-note/siyuan · error

OAuth authorization server does not support dynamic client…

Error message

OAuth authorization server does not support dynamic client registration

What it means

No reusable client registration exists (no stored credential for this issuer/redirect/scopes combination, or it expired), and the authorization server's metadata has no RegistrationEndpoint, so RFC 7591 dynamic client registration is impossible. SiYuan's MCP client relies on DCR to obtain a ClientID, so without it the flow cannot proceed.

Solutions

  1. Use an authorization server that supports RFC 7591 dynamic client registration
  2. If the IdP requires manual registration, pre-register the client and provide the ClientID through the server's configuration mechanism
  3. Re-run authorization on the same SiYuan server port so the stored registration's RedirectURL matches the callback URL
  4. Re-authorize to refresh an expired client registration before scopes change
Defensive patterns

Strategy: validation

Validate before calling

if asm.RegistrationEndpoint == "" {
    return errors.New("IdP lacks RFC 7591 dynamic client registration; register the client manually")
}

Try / catch

if err := h.Authorize(ctx, req, resp); err != nil && strings.Contains(err.Error(), "dynamic client registration") {
    guideManualClientRegistration(err)
}

Prevention

When it happens

Trigger: Interactive Authorize where canReuseRegistration is false (first connect, changed redirect URL because the server port changed, expired registration, or changed scopes) and asm.RegistrationEndpoint == "".

Common situations: IdP without RFC 7591 support (e.g. most enterprise IdPs) — clients must be registered manually; SiYuan restarted on a different port changing the loopback callback URL and invalidating the stored registration; registration record expired.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/9242aa35baca26a5. Report an issue: GitHub.

Appendix: source

Thrown at kernel/mcp/client/oauth.go:287

	if err != nil {
		return err
	}
	callbackURL := fmt.Sprintf("http://127.0.0.1:%s/api/ai/mcp/oauth/callback/%s", util.ServerPort, flowID)
	scopes := append([]string(nil), prm.ScopesSupported...)
	if len(scopes) == 0 {
		scopes = append(scopes, asm.ScopesSupported...)
	}
	for _, scope := range strings.Fields(bearerChallengeParam(challenges, "scope")) {
		if !slices.Contains(scopes, scope) {
			scopes = append(scopes, scope)
		}
	}
	registrationCredential := credential
	canReuseRegistration := hasCredential && credential.Issuer == asm.Issuer && credential.RedirectURL == callbackURL &&
		credential.ClientID != "" && !oauthClientRegistrationExpired(credential) && oauthScopesContain(credential.Scopes, scopes)
	if !canReuseRegistration {
		if asm.RegistrationEndpoint == "" {
			return fmt.Errorf("OAuth authorization server does not support dynamic client registration")
		}
		tokenAuthMethod := preferredTokenAuthMethod(asm.TokenEndpointAuthMethodsSupported)
		if len(asm.TokenEndpointAuthMethodsSupported) > 0 && tokenAuthMethod == "" {
			return fmt.Errorf("OAuth authorization server does not support a compatible token endpoint authentication method")
		}
		grantTypes := []string{"authorization_code"}
		if len(asm.GrantTypesSupported) == 0 || slices.Contains(asm.GrantTypesSupported, "refresh_token") {
			grantTypes = append(grantTypes, "refresh_token")
		}
		registration, registerErr := oauthex.RegisterClient(ctx, asm.RegistrationEndpoint, &oauthex.ClientRegistrationMetadata{
			RedirectURIs:            []string{callbackURL},
			TokenEndpointAuthMethod: tokenAuthMethod,
			GrantTypes:              grantTypes,
			ResponseTypes:           []string{"code"},
			ClientName:              "SiYuan",
			Scope:                   strings.Join(scopes, " "),
			ApplicationType:         "native",
		}, h.client)

View on GitHub (pinned to 9f775e8a12)