Skip to content

Mobile: iOS Voice Memo M4A is routed through video validation and rejected with 422 #5752

Description

@oletillmann

Describe the bug

Buzz Mobile cannot attach an Apple Voice Memo (.m4a). A real iOS upload
failed with HTTP 422:

moov atom not at front of file (not fast-start)

After that failed attempt, retrying surfaced a follow-on error because the
temporary file-provider path no longer existed:

PathNotFoundException: Cannot retrieve length of file, path = '.../Neue Aufnahme 3.m4a'

This is broader than a missing fast-start rewrite. Current main
(a96af89526f7181543e7651100a944aa8e21812b) routes every ISO-BMFF container
into the video pipeline:

fn should_stream_as_video(sniff: &[u8]) -> bool {
    infer::get(sniff).is_some_and(|kind| kind.mime_type() == "video/mp4")
        || buzz_media::looks_like_iso_bmff(sniff)
}

An M4A Voice Memo is ISO-BMFF, so it reaches validate_video_file(). A normal
non-fast-start Voice Memo fails at check_moov_before_mdat() with the observed
422. Merely moving moov would not fully solve the case because the same video
validator later requires a video track.

The generic file path is not a fallback: validate_file_content() explicitly
rejects ISO-BMFF and every detected audio/* until an audio sanitizer exists.
The CLI similarly rejects a synthetic M4A before upload as
unsupported file type: audio/m4a.

Relevant code:

  • crates/buzz-relay/src/api/media.rs (should_stream_as_video, upload_blob)
  • crates/buzz-media/src/validation.rs (validate_file_content,
    validate_video_file, check_moov_before_mdat)
  • mobile/lib/shared/relay/media_upload.dart (uploadFile sends generic files
    as application/octet-stream)

Steps to reproduce

  1. Record a memo in Apple Voice Memos on iPhone.
  2. Attach/share the resulting .m4a into a Buzz Mobile composer as a file.
  3. Send the message.
  4. Observe HTTP 422 with moov atom not at front of file (not fast-start).
  5. Retry after the failed attempt.
  6. Observe that the temporary share/file-provider URL may already be gone,
    producing PathNotFoundException.

A minimal local fixture showing the common layout can be generated with:

ffmpeg -f lavfi -i anullsrc=r=44100:cl=mono -t 1 \
  -c:a aac -b:a 64k -movflags -faststart voice-memo-repro.m4a

Its top-level order is ftyp | mdat | moov. Buzz CLI currently rejects it as
unsupported file type: audio/m4a; the Mobile generic-file route reaches the
relay because it labels the body application/octet-stream.

Expected behavior

Preferred: Buzz accepts a normal AAC/M4A voice memo through an explicit audio
pipeline that remuxes/sanitizes the container, strips metadata, emits a stable
audio/mp4/audio/m4a descriptor, and does not require a video track.

Minimum safe behavior until audio uploads are supported:

  • Detect M4A before enqueue/upload and show a clear "audio attachments are not
    supported yet" message instead of a misleading video fast-start error.
  • Copy file-provider/share-extension inputs into app-owned temporary storage
    before queueing so a failed upload can be retried without
    PathNotFoundException.

Suggested regression coverage

  • Non-fast-start audio-only M4A (ftyp | mdat | moov) does not enter the video
    validator.
  • Fast-start audio-only M4A (ftyp | moov | mdat) does not fail for missing
    video track when audio support is enabled.
  • A queued iOS file-provider attachment remains readable for retry after the
    provider revokes its temporary URL.

Version and platform

  • Buzz version: unknown (reported 2026-08-13; bug is present in current source
    at a96af895)
  • OS: iOS / iPhone

Logs / additional context

No matching open or closed issue was found for m4a, voice memo,
audio upload, moov atom, or fast-start before filing.

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