Skip to content

Use R2 storage for Rendi compressed folder uploads (fal rejects non-media files) #205

@SecurityQQ

Description

@SecurityQQ

Problem

falStorage() (fal.storage.upload()) rejects non-media file types with 400 "Unsupported file format". This breaks the Rendi cloud caption burning pipeline which needs to upload ZIPs containing:

  • ASS subtitle files (text/x-ssa)
  • Font files (.ttf, .otf)
  • Emoji PNGs
  • Video files

The error is intermittent under load — sometimes the first few uploads succeed, then subsequent rapid uploads get rejected. This suggests fal may also have undocumented rate limits on storage uploads.

Current workaround

Switched the Rendi backend's storage from falStorage() to r2Storage() in burn-captions-rendi.test.ts. R2 accepts any file type, has built-in retry logic, and produces stable s3.varg.ai URLs.

For the ASS file specifically, we now pass it as inline data bytes in auxiliaryFiles (avoiding any upload), since it's generated locally.

Changes made

  • src/ai-sdk/providers/editly/backends/types.ts: Extended auxiliaryFiles to accept data?: Uint8Array alongside url?: string
  • src/ai-sdk/providers/editly/rendi/index.ts: runWithCompressedFolder handles inline data entries
  • src/react/renderers/burn-captions.ts: For cloud backends, reads ASS file from disk as inline data (no upload), always uses compressed folder mode
  • src/react/renderers/burn-captions-rendi.test.ts: Uses r2Storage() instead of falStorage()

TODO

  • Ensure the render service (render/) uses r2Storage() when creating the Rendi backend for cloud caption burning
  • Consider adding a temp file upload endpoint to the gateway (POST /v1/upload/temp) for arbitrary file types with automatic TTL-based cleanup
  • Alternatively, standardize on R2 for all intermediate uploads in the Rendi pipeline and document this in the SDK

Context

This was discovered while building E2E tests for caption burning through Rendi. All 24 test scenarios (9 primary fonts, 10 multi-script combos, 5 caption modes including emoji overlays) pass with r2Storage().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions