{"record":{"id":"ceddaeeaed990072","repo":"gitbutlerapp/gitbutler","slug":"is-1-mb-over-the-mb-upload-limit","errorCode":null,"errorMessage":"{} is {:.1} MB, over the {} MB upload limit","messagePattern":"(.+?) is (.+?) MB, over the (.+?) MB upload limit","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-user/src/api.rs","lineNumber":284,"sourceCode":"\n/// Upload a file to the GitButler API and return its public URL.\n///\n/// Calls `POST /api/uploads` with multipart form data using the stored user's\n/// access token. The upload is marked public, because its whole purpose is to be\n/// linked from a review body that the forge renders for anyone who can see it.\n///\n/// Errors when no user is signed in, or when the decoded file exceeds\n/// [`UPLOAD_SIZE_LIMIT`].\npub fn upload_file(params: UploadFileParams) -> Result<serde_json::Value> {\n    let api_url = default_api_url();\n    let token = stored_access_token()?;\n\n    use base64::Engine as _;\n    let bytes = base64::engine::general_purpose::STANDARD\n        .decode(&params.data_base64)\n        .context(\"Invalid base64 in file data\")?;\n    if bytes.len() > UPLOAD_SIZE_LIMIT {\n        anyhow::bail!(\n            \"{} is {:.1} MB, over the {} MB upload limit\",\n            params.filename,\n            bytes.len() as f64 / (1024.0 * 1024.0),\n            UPLOAD_SIZE_LIMIT / (1024 * 1024)\n        );\n    }\n\n    run_async(async move {\n        let url = format!(\"{api_url}/api/uploads\");\n        let client = http_client();\n\n        let mut part = reqwest::multipart::Part::bytes(bytes).file_name(params.filename);\n        if let Some(content_type) = params.content_type {\n            part = part\n                .mime_str(&content_type)\n                .context(\"Invalid content type for upload\")?;\n        }\n        let form = reqwest::multipart::Form::new()","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/gitbutler-user/src/api.rs#L266-L302","documentation":"upload_file in the user API decodes a base64 file payload and enforces UPLOAD_SIZE_LIMIT before sending it to the backend. If the decoded byte length exceeds the limit, it bails with a message naming the file, its size in MB, and the limit in MB.","triggerScenarios":"Calling upload_file with params.data_base64 whose decoded bytes are larger than UPLOAD_SIZE_LIMIT (limit printed in MB in the message).","commonSituations":"Uploading large images/screenshots/attachments for issue reports or AI context without checking size first; user picks a multi-MB file; client compresses poorly or encodes the whole file at once.","solutions":["Check the file size client-side before calling upload_file and reject or downscale anything over the limit (limit is printed in the error)","Compress or resize the payload (e.g. re-encode image, strip EXIF, use JPEG/WebP) then retry","Split the content or upload via a different mechanism that supports larger payloads","Raise UPLOAD_SIZE_LIMIT only if the server genuinely accepts larger files"],"exampleFix":"// before\napi.upload_file(UploadParams { filename, data_base64: b64(huge_bytes) })\n// after\nif huge_bytes.len() > UPLOAD_SIZE_LIMIT { /* compress or reject */ }\napi.upload_file(UploadParams { filename, data_base64: b64(small_bytes) })","handlingStrategy":"validation","validationCode":"let bytes = base64::engine::general_purpose::STANDARD.decode(&params.data_base64)?;\nif bytes.len() > UPLOAD_SIZE_LIMIT {\n    // reject or compress before calling upload_file\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check decoded size before every upload call","Compress/resize images before upload","Show the limit to users in the picker UI","Prefer chunked upload paths for large payloads"],"tags":["upload","file-size","validation","limit"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}