siyuan-note/siyuan · error

OAuth protected resource metadata has no authorization…

Error message

OAuth protected resource metadata has no authorization server

What it means

OAuth protected-resource discovery fetches the resource metadata (RFC 9728) and requires it to list at least one authorization server in the AuthorizationServers array, which is needed to find the AS metadata and run the flow. If the fetched metadata parses but lists no authorization servers, discovery fails with this error.

Solutions

  1. Fix the MCP/protected-resource server to include at least one entry in authorization_servers in its RFC 9728 metadata
  2. Verify you are hitting the correct protected resource metadata URL (check WWW-Authenticate challenge for the resource_metadata URL)
  3. Clear any cached/stale metadata and re-run discovery
  4. If you cannot change the server, supply the authorization server configuration manually or pre-register credentials

Example fix

// before (protected resource metadata)
{"resource":"https://mcp.example.com","authorization_servers":[]}
// after
{"resource":"https://mcp.example.com","authorization_servers":["https://auth.example.com"]}
Defensive patterns

Strategy: validation

Validate before calling

// Fetch and validate the resource metadata before calling Authorize
prm, err := oauthex.GetProtectedResourceMetadata(ctx, metadataURL, resource, client)
if err == nil && len(prm.AuthorizationServers) == 0 {
    return fmt.Errorf("metadata lacks authorization_servers; fix server config")
}

Try / catch

if err := h.Authorize(ctx, false); err != nil {
    if strings.Contains(err.Error(), "no authorization server") {
        // fix the MCP server's RFC 9728 metadata before retrying
    }
}

Prevention

When it happens

Trigger: discoverProtectedResource successfully called oauthex.GetProtectedResourceMetadata for a candidate URL, but prm.AuthorizationServers was empty (len == 0).

Common situations: MCP server publishes incomplete RFC 9728 metadata (missing authorization_servers array); stale/cached metadata after the server's auth setup changed; wrong metadata URL candidate returning a generic/incomplete document.

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 siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/8d3bef3dea18a893. Report an issue: GitHub.

Appendix: source

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

	*oauthex.ProtectedResourceMetadata
	MetadataURL string
}

func discoverProtectedResource(ctx context.Context, challenges []oauthex.Challenge, resource string, client *http.Client) (*discoveredProtectedResource, error) {
	metadataURL := ""
	for _, challenge := range challenges {
		if strings.EqualFold(challenge.Scheme, "bearer") && challenge.Params["resource_metadata"] != "" {
			metadataURL = challenge.Params["resource_metadata"]
			break
		}
	}
	for _, candidate := range protectedResourceURLs(metadataURL, resource) {
		prm, err := oauthex.GetProtectedResourceMetadata(ctx, candidate.URL, candidate.Resource, client)
		if err != nil {
			continue
		}
		if len(prm.AuthorizationServers) == 0 {
			return nil, fmt.Errorf("OAuth protected resource metadata has no authorization server")
		}
		return &discoveredProtectedResource{ProtectedResourceMetadata: prm, MetadataURL: candidate.URL}, nil
	}
	return nil, fmt.Errorf("OAuth protected resource metadata not found")
}

func (h *mcpOAuthHandler) validateCredentialIssuer(ctx context.Context, credential oauthCredential) (bool, error) {
	var challenges []oauthex.Challenge
	resource := h.server.URL
	if credential.ResourceMetadataURL != "" {
		challenges = []oauthex.Challenge{{Scheme: "bearer", Params: map[string]string{"resource_metadata": credential.ResourceMetadataURL}}}
		resource = credential.Resource
	}
	prm, err := discoverProtectedResource(ctx, challenges, resource, h.client)
	if err != nil {
		return false, fmt.Errorf("validate OAuth protected resource: %w", err)
	}
	if prm.Resource != credential.Resource || len(prm.AuthorizationServers) == 0 {

View on GitHub (pinned to 9f775e8a12)