siyuan-note/siyuan · error

save OAuth credentials

Error message

save OAuth credentials: %w

What it means

After a successful token exchange, the completed credential (access/refresh token, expiry, scopes) must be persisted with putOAuthCredential before the token source is installed. This error wraps a storage failure from that save; authorization succeeded but the credentials could not be stored, so the flow reports failure.

Solutions

  1. Inspect the wrapped underlying error to identify the storage failure
  2. Ensure the SiYuan workspace/data directory is writable and has free disk space
  3. Restart the kernel to release any transient store issues and retry the OAuth flow
  4. Restore/repair the credential storage if it is corrupted
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure storage is healthy before running the flow
if err := checkWorkspaceWritable(); err != nil { return err }

Try / catch

if err := h.Authorize(ctx, true); err != nil {
    if strings.Contains(err.Error(), "save OAuth credentials") {
        // fix disk/permission problem, then re-run authorization
    }
}

Prevention

When it happens

Trigger: Authorize() obtained a valid Bearer token and built the final credential, then putOAuthCredential(credential) returned an error while writing it to the credential store.

Common situations: Disk full or read-only workspace; permission changes on the data directory; storage corruption; concurrent write conflicts on the credential store.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: 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 9f775e8a12)