{"record":{"id":"473811cfaede3b39","repo":"jackwener/OpenCLI","slug":"not-a-regular-file-abs","errorCode":null,"errorMessage":"not a regular file: ${abs}","messagePattern":"not a regular file: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/slock/attachment-upload.js","lineNumber":57,"sourceCode":"  strategy: Strategy.COOKIE,\n  browser: true,\n  siteSession: 'persistent',\n  args: [\n    { name: 'file', positional: true, required: true, help: 'Local file path to upload (single file; max 50 MB)' },\n    { name: 'channel', positional: true, required: true, help: 'channelId UUID or #name — server requires the attachment be scoped to a channel' },\n    { name: 'server', help: 'Override active server slug' },\n  ],\n  columns: ['attachmentId', 'filename', 'mimeType', 'sizeBytes'],\n  func: async (page, kwargs) => {\n    const filePath = String(kwargs.file ?? '').trim();\n    if (!filePath) throw new ArgumentError('file path required');\n    const channel = String(kwargs.channel ?? '').trim();\n    if (!channel) throw new ArgumentError('channel required (UUID or #name); server rejects uploads without channelId');\n    const abs = path.resolve(filePath);\n    let stat;\n    try { stat = fs.statSync(abs); }\n    catch (e) { throw new ArgumentError(`file not readable: ${abs} (${e.message})`); }\n    if (!stat.isFile()) throw new ArgumentError(`not a regular file: ${abs}`);\n    if (stat.size === 0) throw new ArgumentError(`file is empty: ${abs}`);\n    if (stat.size > MAX_BYTES) {\n      throw new ArgumentError(`file is ${stat.size} bytes, exceeds server limit ${MAX_BYTES} (50 MiB). Split or compress before upload.`);\n    }\n\n    const buf = fs.readFileSync(abs);\n    const filename = path.basename(abs);\n    const b64 = buf.toString('base64');\n\n    await page.goto(SLOCK_HOME_URL);\n\n    const snippet = `\n      ${authHeadersFragment({ serverScoped: true, serverIdOverride: kwargs.server })}\n      ${channelResolveFragment(channel)}\n      // multipart wants the browser to set its own boundary — strip content-type.\n      const uploadHeaders = { authorization: headers.authorization, accept: headers.accept };\n      if (headers['x-server-id']) uploadHeaders['x-server-id'] = headers['x-server-id'];\n      // Rebuild File from base64 → Uint8Array → Blob → File. The base64 string","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/slock/attachment-upload.js#L39-L75","documentation":"This ArgumentError is thrown by `attachment-upload` in clis/slock/attachment-upload.js:57 when `stat.isFile()` is false — the path exists and is statable, but is not a regular file (a directory, FIFO, socket, device node, etc.). The upload flow reads the whole file into memory and base64-encodes it, which only makes sense for regular files, so non-file paths are rejected early.","triggerScenarios":"Passing a directory path as the `file` argument (e.g. `attachment-upload ./attachments '#general'`), pointing at `/dev/null`, a named pipe, or another special file. Also occurs when a variable that was supposed to hold a file path actually holds a directory.","commonSituations":"Users point at the folder containing the artifact instead of the artifact itself; scripts pass an output directory where a filename was expected; automation accidentally targets device files or sockets produced by another tool.","solutions":["Pass the path to a specific regular file, not a directory: `attachment-upload ./attachments/log.zip '#general'`.","If you meant to upload multiple files, invoke the command once per file (the endpoint caps at 5 files per call and this CLI uploads one at a time).","Check what the path actually is with `file <path>` or `stat -c %F <path>` before uploading in scripts."],"exampleFix":"// before: directory passed instead of a file\nattachment-upload ./build-output '#general'\n// after: upload a specific file within it\nattachment-upload ./build-output/bundle.zip '#general'","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nconst st = fs.statSync(abs);\nif (!st.isFile()) {\n  throw new Error(`${abs} is not a regular file (use a specific file, not a directory)`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await upload({ file: abs, channel });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.startsWith('not a regular file')) {\n    console.error(`${abs} is a directory or special file — pass an individual file path`);\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Point the `file` argument at a concrete file, never a directory or device path.","Confirm the path kind with `stat -c %F <path>` before automating uploads.","Loop over directory contents and upload each file individually if you have a folder of artifacts."],"tags":["argument-error","not-a-file","filesystem","validation"],"backgroundTag":"path-is-not-a-file","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}