Skip to content

Cloud ASR (openai-asr): meetings over ~13 min fail (25 MB upload limit) and unvalidated responses can silently destroy a meeting #403

Description

@Optic00

Follow-up from the #401 review (the PR merged while the review was being finalised, so filing the two release-blocking findings as an issue instead).

1. Any meeting longer than ~13 minutes fails against the default endpoint

_run_openai_asr uploads the whole preprocessed WAV in one request. _preprocess_audio outputs 16 kHz mono pcm_s16le (~32 KB/s), and OpenAI's documented upload limit is 25 MB - so a recording over roughly 13 minutes gets HTTP 413 from api.openai.com (Groq's limit is similar). Meetings are the primary use case and most run longer than that, so as shipped the engine works for short tests and fails for real usage. Chunking exists only in the Parakeet path.

Options, cheapest first:

  • (a) compress the upload with the bundled ffmpeg (64 kbps mono MP3 is ~52 min per 25 MB - still not a 2h meeting);
  • (b) reuse the windowing approach to send chunks and stitch segments - this would also restore a per-chunk HEARTBEAT so a slow request can't trip the inactivity watchdog;
  • (c) at minimum, a preflight size check whose error names the limit.

(b) is the real fix; (c) is the floor.

2. Unvalidated 200 responses can silently destroy a meeting

Responses are trusted without schema validation, and the failure mode is silent data loss rather than an error:

  • A 200 whose JSON lacks text (error envelope from a proxy/shim) yields an empty result indistinguishable from real silence: the pipeline writes the silence sentinel, "succeeds", and - with Save recordings off - deletes the only audio.
  • In the text fallback, a 200 HTML page (captive portal, misconfigured base URL) becomes the transcript verbatim.

Fix: require the expected fields (text key actually present, not defaulted), reject non-JSON/error-shaped bodies with a raised error so the honest transcription_failed path preserves the audio.

Also worth picking up (from the same review)

  • getTranscriptionEnv() injects STENOAI_OAI_API_KEY into every transcription subprocess once a key is stored, even when a local engine is active (the comment claims otherwise). Gate on the configured engine.
  • _do_request embeds the first 500 bytes of HTTP error bodies verbatim in raised errors, which propagate into renderer-visible errors, processing.log, and failure-note frontmatter - a provider/proxy that echoes request headers would persist the Bearer key. Redact token-shaped content.
  • set_openai_asr_api_url accepts plain http:// silently (key + audio cleartext). Require https except loopback, or warn explicitly.
  • Response negotiation is verbose_json → text only; OpenAI's gpt-4o-transcribe family accepts only json, so today's flagship STT model fails both passes. No retry on 429/5xx either. Consider verbose_json → json → text + a small bounded retry.
  • verbose_json returns language as a full name ("english"), which resolve_output_language passes through verbatim where the pipeline expects an ISO code. Normalize, or fall back to the existing text-based detection.
  • A stray empty config.json.lock was committed to the repo root in da1fd68.

Full review context with the praise it deserves (the safeStorage key handling is exemplary): see the #401 thread.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions