{"record":{"id":"4099c98f9273ef9d","repo":"siyuan-note/siyuan","slug":"validate-oauth-protected-resource-w","errorCode":null,"errorMessage":"validate OAuth protected resource: %w","messagePattern":"validate OAuth protected resource: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":523,"sourceCode":"\t\t}\n\t\tif len(prm.AuthorizationServers) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"OAuth protected resource metadata has no authorization server\")\n\t\t}\n\t\treturn &discoveredProtectedResource{ProtectedResourceMetadata: prm, MetadataURL: candidate.URL}, nil\n\t}\n\treturn nil, fmt.Errorf(\"OAuth protected resource metadata not found\")\n}\n\nfunc (h *mcpOAuthHandler) validateCredentialIssuer(ctx context.Context, credential oauthCredential) (bool, error) {\n\tvar challenges []oauthex.Challenge\n\tresource := h.server.URL\n\tif credential.ResourceMetadataURL != \"\" {\n\t\tchallenges = []oauthex.Challenge{{Scheme: \"bearer\", Params: map[string]string{\"resource_metadata\": credential.ResourceMetadataURL}}}\n\t\tresource = credential.Resource\n\t}\n\tprm, err := discoverProtectedResource(ctx, challenges, resource, h.client)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"validate OAuth protected resource: %w\", err)\n\t}\n\tif prm.Resource != credential.Resource || len(prm.AuthorizationServers) == 0 {\n\t\treturn false, nil\n\t}\n\tasm, err := auth.GetAuthServerMetadata(ctx, prm.AuthorizationServers[0], h.client)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"validate OAuth issuer: %w\", err)\n\t}\n\treturn asm != nil && asm.Issuer == credential.Issuer && asm.TokenEndpoint == credential.TokenEndpoint, nil\n}\n\nfunc protectedResourceURLs(metadataURL, resource string) []protectedResourceURL {\n\tvar result []protectedResourceURL\n\tif metadataURL != \"\" {\n\t\tresult = append(result, protectedResourceURL{URL: metadataURL, Resource: resource})\n\t}\n\tresourceURL, err := url.Parse(resource)\n\tif err != nil {","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/oauth.go#L505-L541","documentation":"Wrapped error at oauth.go:521-523 from discoverProtectedResource during validateCredentialIssuer — the routine that confirms a stored credential's issuer is still valid before reuse. The underlying error is one of [335], [336], or a fetch failure, indicating the resource metadata for a previously-working credential can no longer be discovered.","triggerScenarios":"TokenSource calls validateCredentialIssuer on a stored credential; discoverProtectedResource fails (PRM missing, no auth server, or network). The credential is then treated as unusable for that request.","commonSituations":"Resource server moved or dropped RFC 9728 support since the credential was issued; transient network outage to the resource; resource_metadata URL stored on the credential is now 404; AS rotated and the resource no longer advertises it.","solutions":["Confirm the resource server still serves a valid PRM document at the stored ResourceMetadataURL (or the resource's well-known path).","If the resource/issuer changed permanently, clear the stored OAuth credential so SiYuan re-runs full discovery and re-registration.","Retry on transient network failures; for persistent failures, reconfigure the MCP server URL."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// validateCredentialIssuer failure during TokenSource — retry on transient outage.\nvalid, err := h.validateCredentialIssuer(ctx, credential)\nif err != nil {\n    if isTransientNetworkErr(err) {\n        // Allow a short retry; do NOT discard the credential on a transient fetch failure.\n        return nil, nil // fall back to no token this cycle, retry next request\n    }\n    return nil, err\n}","preventionTips":["Distinguish transient discovery failures from permanent resource changes; do not clear credentials on transient errors.","Keep the resource server's PRM endpoint stable across restarts.","When the resource/issuer changes permanently, clear stored OAuth credentials to force clean re-discovery."],"tags":["oauth","mcp","validation","resource-metadata","credential-reuse"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}