{"record":{"id":"5e246edaa152ddb0","repo":"mihomo-party-org/clash-party","slug":"executable-file-not-found-in-zip-exefile","errorCode":null,"errorMessage":"Executable file not found in zip: ${exeFile}","messagePattern":"Executable file not found in zip: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/utils/github.ts","lineNumber":218,"sourceCode":"      // 先停止核心\n      await stopCore(true)\n    }\n\n    // 下载文件\n    await downloadGitHubAsset(downloadURL, tempZip)\n\n    // 解压文件\n    if (urlExt === 'zip') {\n      log.debug(`Extracting ZIP file ${tempZip}`)\n      const zip = new AdmZip(tempZip)\n      const entries = zip.getEntries()\n      const entry = entries.find((e) => e.entryName.includes(exeFile))\n\n      if (entry) {\n        zip.extractEntryTo(entry, coreDir, false, true, false, targetFile)\n        log.debug(`Successfully extracted ${exeFile} to ${targetPath}`)\n      } else {\n        throw new Error(`Executable file not found in zip: ${exeFile}`)\n      }\n    } else {\n      // 处理.gz 文件\n      // 先解压到临时文件再原子替换：直接往 targetPath 写的话，解压中途失败\n      // 会把原本可用的内核截断成半个文件，用户连回退都没得回退。\n      log.debug(`Extracting GZ file ${tempZip}`)\n      const stagingPath = `${targetPath}.download`\n      const readStream = createReadStream(tempZip)\n      const writeStream = createWriteStream(stagingPath)\n\n      try {\n        await new Promise<void>((resolve, reject) => {\n          const gunzip = createGunzip()\n          const onError = (error: Error): void => {\n            log.error('Gzip decompression failed', error)\n            readStream.destroy()\n            gunzip.destroy()\n            writeStream.destroy()","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/utils/github.ts#L200-L236","documentation":"After downloading the Windows .zip distribution of mihomo, installMihomoCore opens it with an unzip library, searches entries for one whose name includes exeFile, and throws 'Executable file not found in zip: <exeFile>' when no entry matches. It means the archive layout changed upstream or the downloaded file is not the expected zip.","triggerScenarios":"installMihomoCore with isWin=true where the extracted zip has no entry containing the expected exe name — mihomo renamed the executable between versions, the zip nests it under a different directory prefix than expected, or the 'zip' is actually an HTML error page saved as .zip (a 200-response mirror).","commonSituations":"Pinning an old/new mihomo version whose binary is named mihomo-windows-amd64-v*.exe vs mihomo.exe; a mirror serving a captcha/error page with 200 status; partial/corrupt download producing a valid-but-empty zip.","solutions":["Re-download from official GitHub (bypass mirrors) to rule out an error page or truncated zip.","Open the downloaded zip and confirm the actual executable name; update the expected exeFile string/PLATFORM_MAP filename to match the release layout.","Switch to a mihomo version whose zip layout matches the expected entry name (renames happen between versions).","Verify zip size against the release asset size before extraction to catch truncated downloads."],"exampleFix":"// before: brittle exact expectation\nconst exeFile = 'mihomo.exe'\n// after: tolerate upstream naming changes\nconst entry = entries.find(\n  (e) => e.entryName.includes(exeFile) || /mihomo.*windows.*\\.(exe)$/i.test(e.entryName)\n)","handlingStrategy":"validation","validationCode":"import AdmZip from 'adm-zip'\n// inspect the archive before extraction to confirm the executable entry exists\nconst zip = new AdmZip(tempZip)\nconst hasExe = zip.getEntries().some((e) => e.entryName.endsWith('.exe'))\nif (!hasExe) {\n  throw new Error('Downloaded zip contains no .exe; likely an error page or wrong asset')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await installSpecificMihomoCore(version)\n} catch (e) {\n  if (String(e).includes('Executable file not found in zip')) {\n    // re-download from official source, or loosen the entry matcher for renamed binaries\n    await reinstallFromOfficialSource(version)\n  } else {\n    throw e\n  }\n}","preventionTips":["Verify downloaded archive size against the release asset size before extraction","Match zip entries by regex (e.g. /mihomo.*windows.*\\.exe$/) instead of a fixed filename","Download from official GitHub to avoid HTML error pages saved as .zip","Test installs against new mihomo versions whenever the upstream renames artifacts"],"tags":["zip","extraction","installation","github-releases","windows"],"backgroundTag":"executable-not-found-in-archive","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}