Back to Support
Uploads

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:

  • Using the exact uploadUrl from the API response
  • Not adding extra headers (like Authorization)
  • Using PUT method (not POST)

  • 6. "File Too Large"


    Each plan has file size limits:

  • Free: 500 MB per file
  • Starter: 2 GB per file
  • Professional: 10 GB per file
  • Enterprise: Custom limits

  • Solution


  • Upgrade your plan
  • Compress the video before uploading
  • Split long videos into segments

  • Best Practices


    ✅ Do This


  • Get presigned URL and upload immediately (within 15 minutes)
  • Use exact Content-Type from your presigned URL request
  • Let the HTTP client calculate Content-Length automatically
  • Confirm upload after completion
  • Handle errors and retry with exponential backoff

  • ❌ Don't Do This


  • Don't cache presigned URLs (they expire)
  • Don't add Authorization header to presigned URL requests
  • Don't change file between requesting URL and uploading
  • Don't modify Content-Type between request and upload

  • 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:

  • The exact error message
  • The file size and type
  • Your API key (first 8 characters)
  • Browser/platform if relevant