{"record":{"id":"640101d60761b5ea","repo":"jackwener/OpenCLI","slug":"not-a-valid-file-abspath","errorCode":null,"errorMessage":"Not a valid file: ${absPath}","messagePattern":"Not a valid file: (.+?)","errorType":"validation","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/post.js","lineNumber":32,"sourceCode":"const SUBMIT_TIMEOUT_MS = 15_000;\nconst COMPOSE_URL = 'https://x.com/compose/post';\nconst FILE_INPUT_SELECTOR = 'input[type=\"file\"][data-testid=\"fileInput\"]';\nconst SUPPORTED_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp']);\n\nfunction validateImagePaths(raw) {\n    const paths = raw.split(',').map(s => s.trim()).filter(Boolean);\n    if (paths.length > MAX_IMAGES) {\n        throw new CommandExecutionError(`Too many images: ${paths.length} (max ${MAX_IMAGES})`);\n    }\n    return paths.map(p => {\n        const absPath = path.resolve(p);\n        const ext = path.extname(absPath).toLowerCase();\n        if (!SUPPORTED_EXTENSIONS.has(ext)) {\n            throw new CommandExecutionError(`Unsupported image format \"${ext}\". Supported: jpg, png, gif, webp`);\n        }\n        const stat = fs.statSync(absPath, { throwIfNoEntry: false });\n        if (!stat || !stat.isFile()) {\n            throw new CommandExecutionError(`Not a valid file: ${absPath}`);\n        }\n        return absPath;\n    });\n}\n\nfunction isUnsupportedInsertTextError(err) {\n    const msg = err instanceof Error ? err.message : String(err);\n    const lower = msg.toLowerCase();\n    return lower.includes('unknown action') || lower.includes('not supported') || lower.includes('inserttext returned no inserted flag');\n}\n\nfunction requirePostActionResult(value, context) {\n    const result = unwrapBrowserResult(value);\n    if (!result || typeof result !== 'object' || Array.isArray(result) || typeof result.ok !== 'boolean') {\n        throw new CommandExecutionError(`${context} returned a malformed result.`);\n    }\n    if (Object.prototype.hasOwnProperty.call(result, 'message') && result.message != null && typeof result.message !== 'string') {\n        throw new CommandExecutionError(`${context} returned a malformed message.`);","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/post.js#L14-L50","documentation":"CommandExecutionError thrown by validateImagePaths when an image path passes the extension check but fs.statSync(absPath, { throwIfNoEntry: false }) returns undefined or a non-file — i.e. the path does not exist, is a directory, or is otherwise not a regular file. throwIfNoEntry prevents statSync itself from throwing so the CLI can produce a clear per-path error naming the resolved absolute path.","triggerScenarios":"Passing a path whose file is missing (deleted/renamed since composing the list), a directory instead of a file, a broken symlink, or a relative path resolved against an unexpected current working directory so path.resolve points somewhere the file isn't.","commonSituations":"Running the command from a different cwd than expected, making relative paths resolve incorrectly; typos in filenames; CI artifacts not yet generated when the command runs; macOS/Linux path-case mismatches; stale file lists from earlier processing steps.","solutions":["Verify each path exists and is a regular file before invoking: fs.statSync(p).isFile().","Use absolute paths (or resolve relative paths against the intended cwd) to avoid cwd surprises.","Fix typos and check case sensitivity of filenames.","Ensure upstream steps that generate the images completed before running the post command."],"exampleFix":"// before\nconst images = 'shot.png,shot.png.bak'.split(',');\nawait post({ text, images: images.join(',') });\n// after\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nconst images = ['shot.png', 'shot2.png']\n  .map(p => path.resolve(p))\n  .filter(p => { try { return fs.statSync(p).isFile(); } catch { return false; } });\nawait post({ text, images: images.join(',') });","handlingStrategy":"validation","validationCode":"import * as fs from 'node:fs';\nconst missing = paths.filter(p => { try { return !fs.statSync(p).isFile(); } catch { return true; } });\nif (missing.length) throw new Error(`Not valid files: ${missing.join(', ')}`);","typeGuard":"function isExistingFile(p) {\n  try { return fs.statSync(p, { throwIfNoEntry: false })?.isFile() === true; }\n  catch { return false; }\n}","tryCatchPattern":"try {\n  await run(['twitter', 'post', '--images', rawImages]);\n} catch (err) {\n  const m = err.message.match(/Not a valid file: (.+)/);\n  if (m) console.error(`Check path exists and is a file: ${m[1]} (cwd: ${process.cwd()})`);\n  else throw err;\n}","preventionTips":["Use absolute paths or run from a known cwd to avoid resolution surprises","Check fs.statSync(p).isFile() for each image before posting","Ensure upstream generation steps completed and files weren't renamed/deleted"],"tags":["validation","file-not-found","twitter","path-resolution"],"backgroundTag":"file-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}