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
- Rename/repackage the plugin so the file has a .cpx extension (use the official packaging command).
- Verify the path points to the plugin package file itself, not a folder or README.
- 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
- Always distribute plugins as .cpx packages and reference the package file, not extracted folders.
- Validate the extension at config-load time, before attempting to load.
- Case-insensitive compare extensions; '.CPX' is accepted by the loader.
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
- Plugin path is not a file
- Plugin file too large
- Plugin file too large
- Invalid plugin URL
- Plugin URL must not contain a fragment
AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30).
Data as JSON: /api/errors/8afc4136c6075dd9.
Report an issue: GitHub.