mihomo-party-org/clash-party · critical · Error

Failed to copy critical file ${file}: ${result.reason}

Error message

Failed to copy critical file ${file}: ${result.reason}

What it means

initFiles copies a set of runtime files in parallel (Promise.allSettled) and inspects each result. If a copy was rejected AND that file is listed in criticalFiles, it throws 'Failed to copy critical file <name>: <reason>' after logging via initLogger. Non-critical file failures are logged but tolerated; critical failures abort startup because the app cannot run without them.

Source

Thrown at src/main/utils/init.ts:278

    {
      name: 'sub-store-frontend',
      targetDirs: [mihomoWorkDir()]
    }
  ]

  const criticalFiles = ['country.mmdb', 'geoip.dat', 'geosite.dat']

  const results = await Promise.allSettled(
    files.map(({ name, targetDirs }) => copyFile(name, targetDirs))
  )

  for (let i = 0; i < results.length; i++) {
    const result = results[i]
    if (result.status === 'rejected') {
      const file = files[i].name
      await initLogger.error(`Failed to copy ${file}`, result.reason)
      if (criticalFiles.includes(file)) {
        throw new Error(`Failed to copy critical file ${file}: ${result.reason}`)
      }
    }
  }
}

async function cleanup(): Promise<void> {
  const [dataFiles, logFiles] = await Promise.all([readdir(dataDir()), readdir(logDir())])

  // 清理更新缓存
  const cacheExtensions = ['.exe', '.pkg', '.7z']
  const cacheCleanup = dataFiles
    .filter((file) => cacheExtensions.some((ext) => file.endsWith(ext)))
    .map((file) => rm(path.join(dataDir(), file)).catch(() => {}))

  // 清理过期日志
  const { maxLogDays = 7 } = await getAppConfig()
  const maxAge = maxLogDays * 24 * 60 * 60 * 1000
  const datePattern = /\d{4}-\d{2}-\d{2}/

View on GitHub (pinned to 911e090537)

Solutions

  1. Read the <reason> suffix: EACCES/EPERM → fix directory permissions; ENOSPC → free disk space; ENOENT → the packaged source file is missing.
  2. If ENOENT, check packaging config (electron-builder extraResources/files) so the template file ships inside the app bundle.
  3. Ensure only one instance of the app runs (single-instance lock) and exclude the config dir from antivirus locking.
  4. Verify the destination config directory exists and is writable by the current user before initFiles runs.
  5. Check initLogger output for the 'Failed to copy <file>' entry to see the full underlying error.

Example fix

// before
await fs.copyFile(src, dest)
// after: ensure destination directory exists first
await fs.mkdir(dirname(dest), { recursive: true })
await fs.copyFile(src, dest)
Defensive patterns

Strategy: try-catch

Validate before calling

import { accessSync, constants, existsSync } from 'fs'
// preflight every critical file: source present, destination dir writable
for (const f of criticalFiles) {
  if (!existsSync(sourcePath(f))) throw new Error(`Bundled source missing for ${f}; check packaging config`)
  accessSync(dirname(destPath(f)), constants.W_OK)
}

Try / catch

try {
  await ensureRuntimeFiles()
} catch (e) {
  const m = String(e).match(/Failed to copy critical file (.+?): (.+)/)
  if (m) {
    const [, file, reason] = m
    if (/EACCES|EPERM/.test(reason)) promptFixPermissions(dirname(destPath(file)))
    else if (/ENOENT/.test(reason)) repairPackagedFiles()
    else if (/ENOSPC/.test(reason)) promptFreeDiskSpace()
  }
  app.quit()
}

Prevention

When it happens

Trigger: ensureRuntimeFiles -> initFiles where a copy promise rejects for a file in criticalFiles: source template missing from the packaged app (asar packaging excluded it), destination directory not writable (permission/EACCES), disk full, or the destination file locked by another running instance/antivirus.

Common situations: macOS translocation or missing extraResources entry so the bundled default config is absent; first run under a user account without write access to the config directory; a previous crashed instance holding a lock; read-only app install on Windows Program Files; disk quota exceeded.

Related errors


AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30). Data as JSON: /api/errors/c23d9e349eb8fe85. Report an issue: GitHub.