{"record":{"id":"8e6f284b02b50314","repo":"jackwener/OpenCLI","slug":"file-is-stat-size-bytes-exceeds-server-limit","errorCode":null,"errorMessage":"file is ${stat.size} bytes, exceeds server limit ${MAX_BYTES} (50 MiB). Split or compress before upload.","messagePattern":"file is (.+?) bytes, exceeds server limit (.+?) \\(50 MiB\\)\\. Split or compress before upload\\.","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/slock/attachment-upload.js","lineNumber":60,"sourceCode":"  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\n      // is the only path across the page boundary; JSON.stringify it (caller did)\n      // so injection isn't possible.\n      const b64 = ${JSON.stringify(b64)};","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/slock/attachment-upload.js#L42-L78","documentation":"This ArgumentError is thrown when the file to upload is larger than MAX_BYTES (50 MiB), the Slock server's attachment size limit. The CLI checks stat.size before reading the file so it fails locally with a clear message instead of a slow upload followed by a server rejection.","triggerScenarios":"Calling attachment-upload with a file whose fs.statSync().size exceeds 50 MiB — e.g. large videos, database dumps, or bundles — while the server enforces a hard 50 MiB cap per attachment.","commonSituations":"Uploading build artifacts or recordings without checking size, datasets that grew since the CLI was written, or automated pipelines that attach whole log archives.","solutions":["Compress the file (zip/gz) and confirm it is now under 50 MiB, then retry.","Split the file into chunks under 50 MiB and upload each as a separate attachment.","Check the size first: `stat -c %s <file>` must be <= 52428800 bytes.","If regular large uploads are needed, host the file externally (object storage) and share a link instead."],"exampleFix":"// before\nawait uploadAttachment(page, '/tmp/big.dump');\n// after\nconst fs = require('fs');\nif (fs.statSync('/tmp/big.dump').size > 50 * 1024 * 1024) {\n  require('child_process').execSync('gzip -f /tmp/big.dump');\n}\nawait uploadAttachment(page, '/tmp/big.dump.gz');","handlingStrategy":"validation","validationCode":"const MAX_BYTES = 50 * 1024 * 1024;\nif (fs.statSync(filePath).size > MAX_BYTES) throw new Error(`${filePath} exceeds 50 MiB server limit`);","typeGuard":"const isWithinUploadLimit = (p, max = 50 * 1024 * 1024) => { try { return fs.statSync(p).size <= max; } catch { return false; } };","tryCatchPattern":"try {\n  await uploadAttachment(page, filePath);\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('exceeds server limit')) {\n    console.error('Compress or split the file before retrying');\n  } else throw e;\n}","preventionTips":["Stat file size and gzip anything over ~40 MiB before upload.","Split large archives into <50 MiB chunks in automation.","Keep the 50 MiB limit (52428800 bytes) as a constant in your scripts."],"tags":["file-upload","size-limit","validation","argument-error"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}