CherryHQ/cherry-studio · error · Error

No code verifier saved for session

Error message

No code verifier saved for session

What it means

Thrown by `getCodeVerifier()` when the OAuth storage data has no `codeVerifier` field. The PKCE code verifier is required to complete the OAuth token exchange — it must match the code_challenge sent during the authorization request. This error means the OAuth flow is attempting token exchange without a prior saveCodeVerifier call.

Source

Thrown at src/main/ai/mcp/oauth/storage.ts:99

  }

  async getTokens(): Promise<OAuthTokens | undefined> {
    const data = await this.readStorage()
    return data.tokens
  }

  async saveTokens(tokens: OAuthTokens | undefined): Promise<void> {
    const data = await this.readStorage()
    await this.writeStorage({
      ...data,
      tokens
    })
  }

  async getCodeVerifier(): Promise<string> {
    const data = await this.readStorage()
    if (!data.codeVerifier) {
      throw new Error('No code verifier saved for session')
    }
    return data.codeVerifier
  }

  async saveCodeVerifier(codeVerifier: string): Promise<void> {
    const data = await this.readStorage()
    await this.writeStorage({
      ...data,
      codeVerifier
    })
  }

  async getAuthServerUrl(): Promise<string | undefined> {
    const data = await this.readStorage()
    return data.authServerUrl
  }

  async saveAuthServerUrl(url: string | undefined): Promise<void> {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Ensure saveCodeVerifier() is called before redirecting the user to the OAuth consent page
  2. If the OAuth flow is being retried, restart from the beginning (generate new verifier + challenge, save, then redirect)
  3. Check that the same JsonFileStorage instance (same serverUrlHash) is used for both save and get
  4. If storage was corrupted or cleared, restart the OAuth flow entirely rather than trying to resume
Defensive patterns

Strategy: validation

Validate before calling

// Before attempting OAuth callback, verify verifier exists
try {
  await oauthStorage.getCodeVerifier()
} catch (e) {
  if (e instanceof Error && e.message === 'No code verifier saved for session') {
    // Restart the OAuth flow from scratch
    throw new Error('OAuth session expired. Please re-authenticate.')
  }
  throw e
}

Try / catch

try {
  const verifier = await oauthStorage.getCodeVerifier()
  await transport.finishAuth(authCode)
} catch (e) {
  if (e instanceof Error && e.message === 'No code verifier saved for session') {
    // PKCE state lost — restart the entire OAuth flow
    return restartOAuthFlow()
  }
  throw e
}

Prevention

When it happens

Trigger: The OAuth callback handler calls `getCodeVerifier()` but `saveCodeVerifier()` was never called or the storage was cleared between the authorization request and the callback. This breaks the PKCE flow since the verifier needed for token exchange is missing.

Common situations: The OAuth flow was interrupted and retried, but the retry doesn't call saveCodeVerifier; storage was cleared mid-flow; the authorization request was initiated by a different storage instance than the one handling the callback; a race condition where clear() runs between save and get.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/cda19c82f257a41c. Report an issue: GitHub.