Common Upload Errors and Solutions
Troubleshoot upload failures and learn best practices
Common Upload Errors and Solutions
Uploading to FFmpeg Micro uses a presigned URL flow. Here are the most common issues and how to fix them.
1. "SignatureDoesNotMatch" Error
This error means the upload request doesn't match the presigned URL signature.
Common Causes
Wrong Content-Type:
# ❌ WRONG - Content-Type doesn't match what you requested
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/mpeg" \
--data-binary @video.mp4
# ✅ CORRECT - matches the contentType from presigned URL request
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/mp4" \
--data-binary @video.mp4
Extra headers:
# ❌ WRONG - Authorization header not allowed on presigned URL
curl -X PUT "$UPLOAD_URL" \
-H "Authorization: Bearer YOUR_API_KEY" \
--data-binary @video.mp4
# ✅ CORRECT - only Content-Type header
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: video/mp4" \
--data-binary @video.mp4
Solution
Only include the Content-Type header. The presigned URL already contains authentication.
2. "Expired Presigned URL"
Presigned upload URLs expire after 15 minutes.
Error Message
<Error>
<Code>ExpiredToken</Code>
<Message>The provided token has expired.</Message>
</Error>
Solution
Request a fresh presigned URL and upload immediately:
// ❌ WRONG - URL could expire
const { uploadUrl } = await getPresignedUrl()
await doSomethingElse() // Takes 20 minutes...
await uploadFile(uploadUrl, file) // Expired!
// ✅ CORRECT - upload immediately
const { uploadUrl } = await getPresignedUrl()
await uploadFile(uploadUrl, file) // Within 15 minutes
3. "Network Timeout"
Large files can timeout if your connection is slow.
Solutions
1. Use resumable uploads (for files > 100MB):
// Request a resumable upload URL
const response = await fetch('/v1/upload/presigned-url', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
filename: 'large-video.mp4',
contentType: 'video/mp4',
fileSize: file.size,
resumable: true // ← Enable resumable uploads
})
})
2. Compress before uploading:
# Compress video before upload (reduces size by ~50%)
ffmpeg -i input.mp4 -vcodec libx264 -crf 28 compressed.mp4
3. Upload in chunks (for very large files):
Contact support for multipart upload instructions.
4. "Invalid Bucket Permissions"
If you're uploading to your own bucket, you may see permission errors.
Solution
Use the FFmpeg Micro presigned upload flow instead of your own bucket. Or contact support to configure bucket permissions.
5. CORS Errors (Browser Uploads)
If you're uploading from a browser and see CORS errors:
Error Message
Access to fetch at 'https://storage.googleapis.com/...'
from origin 'https://your-app.com' has been blocked by CORS policy
Solution
The presigned URLs already include CORS headers. Make sure you're:
uploadUrl from the API responsePUT method (not POST)6. "File Too Large"
Each plan has file size limits:
Solution
Best Practices
✅ Do This
Content-Type from your presigned URL requestContent-Length automatically❌ Don't Do This
Example: Robust Upload Flow
async function uploadVideo(file) {
const maxRetries = 3
let attempt = 0
while (attempt < maxRetries) {
try {
// 1. Get presigned URL
const urlResponse = await fetch('/v1/upload/presigned-url', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
filename: file.name,
contentType: file.type,
fileSize: file.size
})
})
if (!urlResponse.ok) {
throw new Error('Failed to get presigned URL')
}
const { uploadUrl, filename } = await urlResponse.json()
// 2. Upload file (no auth header!)
const uploadResponse = await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': file.type
},
body: file
})
if (!uploadResponse.ok) {
throw new Error('Upload failed: ' + uploadResponse.statusText)
}
// 3. Confirm upload
await fetch('/v1/upload/confirm', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
filename: filename,
fileSize: file.size
})
})
return filename
} catch (error) {
attempt++
if (attempt >= maxRetries) {
throw error
}
// Exponential backoff: 2s, 4s, 8s
await new Promise(resolve => setTimeout(resolve, 2000 * Math.pow(2, attempt)))
}
}
}
Still Having Issues?
Contact support at javid@ffmpeg-micro.com with:
Can't find what you're looking for?