{"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/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L505-L541","documentation":"validateCredentialIssuer wraps any failure from discoverProtectedResource with the 'validate OAuth protected resource' prefix. It means the stored OAuth credential could not be re-validated because the protected resource metadata could not be fetched (or reported no authorization server). The credential itself is left untouched; validation simply returns false with this error.","triggerScenarios":"TokenSource calls validateCredentialIssuer before reusing a stored credential. Thrown when discoverProtectedResource returns the 'metadata not found' or 'metadata has no authorization server' error — i.e. metadata fetch fails for all candidate URLs during credential re-validation.","commonSituations":"The MCP server went offline or was reconfigured to drop OAuth metadata; temporary network outage while refreshing a token; credential's ResourceMetadataURL now 404s after a server upgrade; DNS or proxy issues.","solutions":["Check server reachability and that its /.well-known/oauth-protected-resource metadata endpoint still returns valid JSON.","Retry later if it is a transient network error — validation failures are not necessarily permanent.","Re-authenticate from scratch (start a new Authorize flow) so fresh metadata URLs are discovered and stored.","Update the stored credential's ResourceMetadataURL if the server moved its metadata endpoint.","Inspect the wrapped error (%w chain) for the real fetch failure cause."],"exampleFix":"// before: assuming stored credential is always valid\n// after: handle validation failure by falling back to a fresh authorize flow\nvalid, err := handler.validateCredentialIssuer(ctx, credential)\nif err != nil || !valid {\n    credential, err = handler.authorize(ctx) // re-run OAuth discovery\n}","handlingStrategy":"retry","validationCode":"if _, err := http.Get(credential.ResourceMetadataURL); err != nil {\n    // defer re-validation until network/IdP is reachable\n}","typeGuard":null,"tryCatchPattern":"valid, err := handler.validateCredentialIssuer(ctx, credential)\nif err != nil {\n    if isTransient(err) { scheduleRetry() } else { startFreshAuthorizeFlow() }\n}","preventionTips":["Distinguish transient fetch failures from permanent metadata removal via the wrapped error","Re-run discovery from scratch instead of trusting stored metadata URLs after server upgrades","Monitor MCP server availability before token refresh windows","Keep ResourceMetadataURL in sync with the server's actual metadata location"],"tags":["oauth","mcp","discovery","network"],"backgroundTag":"http-request-failed","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}