siyuan-note/siyuan · error
save OAuth credentials: %w
Error message
save OAuth credentials: %w
What it means
Wrapped error at oauth.go:413-414 when putOAuthCredential fails to persist the final token credential after a successful authorization-code exchange. The tokens were obtained but cannot be stored, so the next request will not have valid credentials and the flow must be repeated.
Source
Thrown at kernel/mcp/client/oauth.go:414
token, err := config.Exchange(exchangeCtx, callback.Code,
oauth2.VerifierOption(verifier),
oauth2.SetAuthURLParam("resource", prm.Resource))
if err != nil {
return fmt.Errorf("exchange OAuth authorization code: %w", err)
}
if token.TokenType != "" && !strings.EqualFold(token.TokenType, "Bearer") {
return fmt.Errorf("OAuth token endpoint returned unsupported token type %q", token.TokenType)
}
credential = registrationCredential
credential.TokenAuthMethod = authMethod
credential.AccessToken = token.AccessToken
credential.RefreshToken = token.RefreshToken
credential.TokenType = token.TokenType
credential.Expiry = token.Expiry
credential.Scopes = scopes
credential.Rejected = false
if err = putOAuthCredential(credential); err != nil {
return fmt.Errorf("save OAuth credentials: %w", err)
}
h.sourceMu.Lock()
h.source = &storedOAuthTokenSource{credential: credential, client: h.client}
h.sourceMu.Unlock()
setMCPRuntimeStateForContext(ctx, h.server.ID, "oauth_retrying", 0, "", "")
return nil
}
func hasBearerChallenge(challenges []oauthex.Challenge) bool {
for _, challenge := range challenges {
if strings.EqualFold(challenge.Scheme, "bearer") {
return true
}
}
return false
}
func bearerChallengeParam(challenges []oauthex.Challenge, name string) string {View on GitHub (pinned to 251596fc0d)
Solutions
- Check the wrapped error's filesystem errno and resolve space/permissions on the kernel data directory.
- Ensure single-instance operation (no second SiYuan holding the workspace lock).
- After resolving the storage issue, re-run authorization — the in-memory source was not installed (h.source set only after a successful save).
Defensive patterns
Strategy: try-catch
Try / catch
if err = putOAuthCredential(credential); err != nil {
// Final token obtained but not persisted; user must re-authorize after storage is fixed.
logging.LogErrorf("mcp oauth: persist credentials failed: %s", err)
return fmt.Errorf("save OAuth credentials: %w", err)
} Prevention
- Maintain writable, sufficiently sized kernel data directory.
- Single-instance operation per workspace to prevent keystore lock contention.
- Monitor filesystem health to catch credential-save failures early.
When it happens
Trigger: putOAuthCredential returns non-nil right after the token fields (AccessToken, RefreshToken, Expiry, Scopes, TokenType) are populated on the credential. Identical storage failure mode as error 327 but at the final-save step.
Common situations: Disk full; keystore file locked by another process; permissions revoked on the data directory; keystore corruption; AV locking the credential file on Windows.
Related errors
- save OAuth client registration: %w
- mcp oauth authorization required
- parse OAuth challenge: %w
- server returned %s without an OAuth Bearer challenge
- server returned %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/fbead83d8d9bc82c.
Report an issue: GitHub.