{"record":{"id":"e303f338360f893e","repo":"jackwener/OpenCLI","slug":"label-ext-array-from-allowedexts","errorCode":null,"errorMessage":"不支持的${label}格式: ${ext}（支持 ${Array.from(allowedExts).join('/')}）","messagePattern":"不支持的(.+?)格式: (.+?)（支持 (.+?)）","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/wechat-channels/publish.js","lineNumber":77,"sourceCode":"\nasync function evalPage(page, script) {\n  return unwrapEvaluateResult(await page.evaluate(script));\n}\n\nfunction requireFilePath(filePath, label, allowedExts) {\n  const resolved = path.resolve(String(filePath));\n  let stat;\n  try {\n    stat = fs.statSync(resolved);\n  } catch {\n    throw new ArgumentError(`${label}文件不存在: ${resolved}`);\n  }\n  if (!stat.isFile()) {\n    throw new ArgumentError(`${label}路径不是文件: ${resolved}`);\n  }\n  const ext = path.extname(resolved).toLowerCase();\n  if (!allowedExts.has(ext)) {\n    throw new ArgumentError(`不支持的${label}格式: ${ext}（支持 ${Array.from(allowedExts).join('/')}）`);\n  }\n  return resolved;\n}\n\nfunction parseTimeoutSeconds(raw) {\n  const timeout = raw == null || raw === '' ? 600 : Number(raw);\n  if (!Number.isInteger(timeout) || timeout < 30) {\n    throw new ArgumentError('--timeout must be an integer >= 30 seconds');\n  }\n  return timeout;\n}\n\nfunction parseScheduleDate(raw) {\n  if (!raw) return null;\n  const dt = typeof raw === 'number'\n    ? new Date(raw < 1e12 ? raw * 1000 : raw)\n    : new Date(String(raw));\n  if (Number.isNaN(dt.getTime())) {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/wechat-channels/publish.js#L59-L95","documentation":"requireFilePath checks the resolved path's extension against the allowed set (.mp4/.mov/.avi/.webm) and throws ArgumentError when it does not match. WeChat Channels only accepts video uploads, so unsupported formats are rejected before any browser automation begins.","triggerScenarios":"Passing a video with an extension outside {.mp4,.mov,.avi,.webm} (e.g. .mkv, .flv, .ts, .m4v) or a file with no extension via videoPath/requireFilePath.","commonSituations":"Users exporting MKV from OBS; uppercase extensions are fine (lowercased) but formats like .m4v or .wmv are not; generated temp files missing extensions.","solutions":["Convert the video to MP4 (e.g. ffmpeg -i input.mkv -c copy output.mp4).","Use one of the supported extensions: .mp4, .mov, .avi, .webm.","Rename only if the container actually matches the extension (renaming .mkv to .mp4 will fail at upload)."],"exampleFix":"// before\nnode publish.js --video screen-recording.mkv\n// after\nffmpeg -i screen-recording.mkv -c:v libx264 -c:a aac screen-recording.mp4\nnode publish.js --video screen-recording.mp4","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['.mp4', '.mov', '.avi', '.webm']);\nconst ext = require('path').extname(videoPath).toLowerCase();\nif (!ALLOWED.has(ext)) {\n  throw new Error(`Unsupported extension ${ext}; convert to .mp4 first`);\n}","typeGuard":"function hasAllowedVideoExt(p) {\n  return ['.mp4', '.mov', '.avi', '.webm'].includes(require('path').extname(p).toLowerCase());\n}","tryCatchPattern":"try {\n  await publish({ videoPath });\n} catch (e) {\n  if (/不支持的.*格式/.test(e.message)) {\n    console.error('Convert with: ffmpeg -i in.<ext> -c copy in.mp4');\n  }\n  throw e;\n}","preventionTips":["Standardize on MP4 (H.264/AAC) exports for all uploads.","Check extensions in CI/pipeline scripts before publishing.","Don't just rename containers — actually transcode with ffmpeg."],"tags":["cli","argument-validation","video-format"],"backgroundTag":"unsupported-file-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}