{"record":{"id":"68ba0957874d829e","repo":"CherryHQ/cherry-studio","slug":"packagelabel-file-not-found","errorCode":null,"errorMessage":"${packageLabel} file not found","messagePattern":"(.+?) file not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/McpPackageService.ts","lineNumber":512,"sourceCode":"      await fs.promises.writeFile(tempPath, fileData)\n      return await this.uploadPackage(tempPath, packageFormat)\n    } catch (error) {\n      logger.error(`${packageLabel} upload error:`, error as Error)\n      return {\n        success: false,\n        error: error instanceof Error ? error.message : `Failed to upload ${packageLabel} file`\n      }\n    }\n  }\n\n  private async uploadPackage(filePath: string, packageFormat: McpPackageFormat): Promise<McpPackageUploadResult> {\n    const packageLabel = packageFormat === 'mcpb' ? 'MCPB' : 'DXT'\n    const tempExtractDir = path.join(this.tempDir, `${packageFormat}_${uuidv4()}`)\n\n    try {\n      // Validate file exists\n      if (!fs.existsSync(filePath)) {\n        throw new Error(`${packageLabel} file not found`)\n      }\n\n      // Extract the package file (which is a ZIP archive) to a temporary directory\n      logger.debug(`Extracting ${packageLabel} file: ${filePath}`)\n\n      const zip = new StreamZip.async({ file: filePath })\n      try {\n        // Reject any zip-slip entry before writing anything to disk.\n        assertZipEntriesWithin(Object.keys(await zip.entries()), tempExtractDir)\n        await zip.extract(null, tempExtractDir)\n      } finally {\n        await zip.close()\n      }\n\n      // Read and validate the manifest.json\n      const manifestPath = path.join(tempExtractDir, 'manifest.json')\n      if (!fs.existsSync(manifestPath)) {\n        throw new Error(`manifest.json not found in ${packageLabel} file`)","sourceCodeStart":494,"sourceCodeEnd":530,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/McpPackageService.ts#L494-L530","documentation":"Thrown near the top of uploadPackage's try block when fs.existsSync(filePath) returns false. uploadPackage is the shared extract/install path invoked after uploadFromBuffer stages the buffer to a temp file; it is also the natural place a direct file-path install would start. The check is a precondition guard before StreamZip opens the file.","triggerScenarios":"The temp file written by uploadFromBuffer was removed before uploadPackage ran (race with another cleanup, antivirus quarantine, the service cleanup() running concurrently); or uploadPackage was called directly with a path that does not exist.","commonSituations":"Concurrent calls to McpPackageService.cleanup() (e.g. on app shutdown) deleted the temp dir mid-upload; an antivirus or sync tool quarantined the freshly written temp file; a developer invoked uploadPackage in a test with a non-existent path.","solutions":["Avoid calling service.cleanup() while uploads are in flight; ensure the lifecycle stops the service only after pending uploads complete.","If the temp dir is being cleared by an external process, move the temp dir (application.getPath('feature.dxt.uploads.temp')) to a location not scanned by AV/sync.","Retry the upload; if it fails again, inspect main-process logs for concurrent cleanup or shutdown around the same timestamp."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import * as fs from 'fs'\nfunction fileExistsAtPath(filePath: string): boolean {\n  return fs.existsSync(filePath)\n}","typeGuard":null,"tryCatchPattern":"// uploadFromBuffer/uploadPackage already wraps this in try/catch and returns { success: false, error }.\n// Callers of uploadDxt/uploadMcpb should check the result shape, not await a throw.\nconst result = await mcpPackageService.uploadDxt(buf, name)\nif (!result.success) {\n  notifyUser(result.error ?? 'Upload failed')\n}","preventionTips":["Do not call McpPackageService.cleanup() concurrently with uploads; gate cleanup on pending upload completion.","If an external process (AV, sync) quarantines files in the temp dir, relocate the temp dir to a non-scanned path.","When calling uploadPackage directly in tests, assert fs.existsSync(filePath) before invoking."],"tags":["mcp","upload","filesystem","race-condition"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}