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

Unsupported plugin file type

Error message

Unsupported plugin file type

What it means

readPluginFile() only accepts plugin packages with the .cpx extension. Before touching the filesystem it checks extname(filePath) and throws immediately when it is anything else, because the plugin loader cannot safely parse or trust a payload from an unrecognized container format.

Source

Thrown at src/main/resolve/plugin/file.ts:12

import { open } from 'fs/promises'
import { basename, extname, resolve } from 'path'
import { MAX_PLUGIN_FILE_BYTES } from './constants'

export function findPluginFile(args: string[]): string | undefined {
  return args.find(
    (arg) => !arg.startsWith('-') && !arg.includes('://') && extname(arg).toLowerCase() === '.cpx'
  )
}

export async function readPluginFile(filePath: string): Promise<IPluginFilePayload> {
  if (extname(filePath).toLowerCase() !== '.cpx') throw new Error('Unsupported plugin file type')

  const resolvedPath = resolve(filePath)
  const handle = await open(resolvedPath, 'r')
  try {
    const stat = await handle.stat()
    if (!stat.isFile()) throw new Error('Plugin path is not a file')
    if (stat.size > MAX_PLUGIN_FILE_BYTES) throw new Error('Plugin file too large')

    // Read at most one byte beyond the limit so a file growing after stat cannot bypass the cap.
    const bytes = Buffer.alloc(MAX_PLUGIN_FILE_BYTES + 1)
    let length = 0
    while (length < bytes.length) {
      const { bytesRead } = await handle.read(bytes, length, bytes.length - length, null)
      if (bytesRead === 0) break
      length += bytesRead
    }
    if (length > MAX_PLUGIN_FILE_BYTES) throw new Error('Plugin file too large')

View on GitHub (pinned to 911e090537)

Solutions

  1. Rename/repackage the plugin so the file has a .cpx extension (use the official packaging command).
  2. Verify the path points to the plugin package file itself, not a folder or README.
  3. Check the plugin source — you may have downloaded the wrong asset (source tarball vs .cpx package).

Example fix

// before
await readPluginFile('/downloads/my-plugin.zip')
// after
await readPluginFile('/downloads/my-plugin.cpx')
Defensive patterns

Strategy: validation

Validate before calling

import { extname } from 'node:path'
if (extname(pluginPath).toLowerCase() !== '.cpx') {
  throw new Error(`Expected a .cpx plugin package, got: ${pluginPath}`)
}

Type guard

const isCpxPath = (p: string): boolean => extname(p).toLowerCase() === '.cpx'

Try / catch

try {
  const payload = await readPluginFile(pluginPath)
} catch (e) {
  if ((e as Error).message === 'Unsupported plugin file type') {
    // surface a friendly message about the .cpx requirement
  } else throw e
}

Prevention

When it happens

Trigger: Calling readPluginFile (or the payload helper that wraps it) with a path whose extension is not .cpx — e.g. a .zip, .js, or extension-less path. The check is case-insensitive, so '.CPX' is fine.

Common situations: Users pointing the plugin resolver at a raw zip archive downloaded manually, a plugin directory instead of the packaged file, or an older plugin format that predates the .cpx packaging standard.

Related errors


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