jackwener/OpenCLI · error · ArgumentError
weixin create-draft cover-image is not a file: ${absPath}
Error message
weixin create-draft cover-image is not a file: ${absPath} What it means
The path exists but stat.isFile() is false — the cover image points to a directory or special file. The library rejects it with this ArgumentError since the WeChat draft API needs an actual image file to upload.
Source
Thrown at clis/weixin/create-draft.js:44
}
function isRecoverableFileInputError(error) {
const message = error instanceof Error ? error.message : String(error);
return /unknown action|not supported|not[-\s]?allowed|notallowederror/i.test(message);
}
function resolveCoverImage(rawPath) {
const value = String(rawPath ?? '').trim();
if (!value) throw new ArgumentError('weixin create-draft cover-image cannot be empty');
const absPath = path.resolve(value);
let stat;
try {
stat = fs.statSync(absPath);
} catch {
throw new ArgumentError(`weixin create-draft cover-image does not exist: ${absPath}`);
}
if (!stat.isFile()) {
throw new ArgumentError(`weixin create-draft cover-image is not a file: ${absPath}`);
}
const extension = path.extname(absPath).toLowerCase();
const mimeType = IMAGE_MIME_TYPES.get(extension);
if (!mimeType) {
throw new ArgumentError('weixin create-draft cover-image must be JPEG, PNG, GIF, or WebP');
}
return { absPath, fileName: path.basename(absPath), mimeType };
}
async function navigateToEditor(page) {
await page.goto(WEIXIN_HOME);
await page.wait(3);
const token = await evaluate(page, `(window.location.href.match(/token=(\\d+)/)||[])[1]`);
if (!token) {
throw new AuthRequiredError(WEIXIN_DOMAIN, 'Please log in to the WeChat Official Account platform and retry.');
}
await page.goto(`https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit_v2&action=edit&isNew=1&type=77&token=${token}&lang=zh_CN`);
await page.wait(4);View on GitHub (pinned to 49907e53dc)
Solutions
- Point cover-image at an actual image file, not a directory.
- If using a symlink, confirm it resolves to a regular file.
- Print/inspect the resolved path in the error to see what was actually passed.
Example fix
// before coverImage: './assets' // after coverImage: './assets/cover.jpg'
Defensive patterns
Strategy: validation
Validate before calling
const st = fs.statSync(path.resolve(coverImage));
if (!st.isFile()) throw new Error('cover-image must be a regular file, not a directory'); Type guard
function isRegularFile(p) {
try { return fs.statSync(p).isFile(); } catch { return false; }
} Try / catch
try {
await createDraft({ coverImage });
} catch (err) {
if (err instanceof ArgumentError && err.message.includes('is not a file')) {
console.error('coverImage must point to a file, not a directory');
} else throw err;
} Prevention
- Never point cover-image at a directory
- Validate symlinks resolve to files
- Sanitize upstream config values
- Resolve globs to concrete file paths before calling
When it happens
Trigger: Passing a directory path as cover-image; passing a device/fifo/socket path; on some setups a symlink pointing at a directory (statSync follows symlinks).
Common situations: Pointing cover-image at an assets folder instead of a file; shell glob expanding oddly; variable holding a directory from upstream config.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- not a regular file: ${abs}
- weixin create-draft cover-image does not exist: ${absPath}
- 封面文件不存在: ${path.resolve(coverPath)}
- file not readable: ${abs} (${e.message})
- file is empty: ${abs}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/b5da2f2f80dd6e03.
Report an issue: GitHub.