{"record":{"id":"3b0fc838d07dbb14","repo":"CherryHQ/cherry-studio","slug":"failed-to-write-oauth-storage-error-instanceof","errorCode":null,"errorMessage":"Failed to write OAuth storage: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Failed to write OAuth storage: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/oauth/storage.ts","lineNumber":66,"sourceCode":"\n  private async writeStorage(data: OAuthStorageData): Promise<void> {\n    try {\n      // Ensure directory exists\n      await fs.mkdir(path.dirname(this.filePath), { recursive: true })\n\n      // Update timestamp\n      data.lastUpdated = Date.now()\n\n      // Write file atomically\n      const tempPath = `${this.filePath}.tmp`\n      await fs.writeFile(tempPath, JSON.stringify(data, null, 2))\n      await fs.rename(tempPath, this.filePath)\n\n      // Update cache\n      this.cache = data\n    } catch (error) {\n      logger.error('Error writing OAuth storage:', error as Error)\n      throw new Error(`Failed to write OAuth storage: ${error instanceof Error ? error.message : String(error)}`)\n    }\n  }\n\n  async getClientInformation(): Promise<OAuthClientInformation | undefined> {\n    const data = await this.readStorage()\n    return data.clientInfo\n  }\n\n  async saveClientInformation(info: OAuthClientInformationMixed | undefined): Promise<void> {\n    const data = await this.readStorage()\n    await this.writeStorage({\n      ...data,\n      clientInfo: info\n    })\n  }\n\n  async getTokens(): Promise<OAuthTokens | undefined> {\n    const data = await this.readStorage()","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/oauth/storage.ts#L48-L84","documentation":"Thrown by `writeStorage()` when the atomic write (write temp file → rename to final path) fails. The method creates the parent directory, writes to a `.tmp` file, then renames atomically. Any failure in mkdir, writeFile, or rename triggers this. The cache is not updated on failure, so the in-memory state stays consistent with the last successful write.","triggerScenarios":"Disk full preventing the temp file write; permission denied on the config directory; the rename fails across filesystems (temp and target on different mounts); the directory was deleted between mkdir and writeFile; disk I/O hardware error.","commonSituations":"Disk space exhausted; config directory permissions changed since app start; running from a read-only filesystem; antivirus intercepting the rename on Windows; the OS temp directory and config directory are on different volumes making rename fail.","solutions":["Check available disk space on the volume containing the MCP config directory","Verify write permissions on the config directory (usually under userData/mcp/)","If the rename fails due to cross-filesystem, ensure the .tmp file is created in the same directory as the target (which it is — check for symlink interference)","Retry the operation — transient I/O errors may resolve; if persistent, investigate disk health"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await oauthStorage.saveTokens(tokens)\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Failed to write OAuth storage')) {\n    // Check disk space and permissions, then retry\n    logger.error('OAuth storage write failed', e)\n    // Non-fatal: tokens stay in memory, will be retried on next save\n  }\n  throw e\n}","preventionTips":["Ensure adequate disk space in the userData directory","Verify write permissions on the MCP config directory during app startup","Monitor write failures — if persistent, alert the user about storage issues"],"tags":["mcp","oauth","storage","file-io","disk-space"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}