The recording bug we fixed three times
The timer moved. The meter reacted. Finish worked. For six days after launch, not one recording completed the path to a transcript.

Scripta shipped browser recording on August 13, 2026. It completed zero recordings until August 19.
The recorder itself looked healthy. It obtained audio, displayed a timer, moved the level meter, and let the person press Finish. Then three independent faults stopped the recording at successive boundaries. Each fix exposed the next one, so “we fixed the recording bug” was wrong twice before it was true.
The visible feature was only the first stage
A browser recorder can succeed while the feature fails.
The full path was:
- capture audio in the browser;
- upload it to private storage;
- register an authorized recording row;
- read the stored audio for transcription;
- send one batch request to xAI Grok STT;
- save a usable transcript.
The launch checks proved the first step. The product promise depended on all six.
Fault one: the container and bucket disagreed
The browser selected audio/webm;codecs=opus. The private storage bucket allowed audio/webm.
Those describe the same base media type, but the bucket used an exact-string allowlist. The codec parameter made the captured file fail after the meeting had already ended.
The interface translated the failure into a generic connection message. That sent everyone toward Wi-Fi even though the network was not the problem.
The durable correction was to normalize the media type at the storage boundary while preserving the recorder's actual type where it remained useful. The boundary needed the base type; the diagnostic record could retain the detail.
Fault two: valid SQL named the wrong column
After the MIME fix, storage authorization failed.
An RLS policy used an unqualified name inside an EXISTS expression. SQL resolved it to the wrong relation instead of storage.objects.name. The policy parsed and deployed. It simply denied the intended operation.
Qualification can look like style until the query crosses a security boundary. There, it is correctness. The policy needed to say which relation owned the path being checked.
Fixing that revealed the final fault.
Fault three: two provider fields belonged together
The audio now reached xAI, which refused the transcription request. The request included format without language; the provider required the pair together.
This was not a problem a retry could solve. The request builder had allowed an invalid combination.
The correction was structural: construct the related parameters together so call sites cannot separate them. When two fields form an invariant, the code should make the incomplete state difficult to express.
One generic sentence hid three systems
All three failures looked like a connection problem to the person recording. That message was meant to be friendly. During a new integration, it destroyed the only clue about which boundary had failed.
A storage allowlist, an RLS policy, and a provider request do not have the same remedy. Flattening them into one sentence makes support calmer and debugging slower.
The better launch posture is to surface the upstream error safely and preserve it on the recording. A person should know whether their audio is saved and whether transcription can be retried. Engineers should know which system refused the operation.
Monitor outcomes, not healthy components
The missing metric was simple: how many recordings reached a completed transcript?
Route health would not answer it. Recorder initialization would not answer it. Upload attempts would not answer it. A feature that crossed browser, storage, database policy, and an external model needed one end-to-end success definition.
That lesson applies beyond recording:
- test the exact media type a real browser emits;
- exercise policies against the deployed relation names;
- test external requests with valid parameter combinations;
- keep failed recordings recoverable;
- monitor the user outcome, not just each component's availability.
Unit tests remain valuable, but the deployed path must run at least once before a multi-system feature can be called working.
The record begins after capture succeeds
Once transcription completes, the transcript still arrives as material for review. A person promotes it to the meeting. The minutes assistant drafts from it, and consequential extractions point back to quotes verified in the transcript.
That downstream care is irrelevant if the source never survives the pipeline. Meeting notes can only show their work after the recording system has produced evidence worth reviewing.
The six-day failure changed the engineering definition of “recording works” from a moving meter to a completed, recoverable transcript. Nothing of Scripta's ever joined the call; the failures lived in the path that began after browser capture.
FAQ
Why did the recorder appear to work?
Why not upload every MediaRecorder chunk separately?
Did a Scripta bot join the meeting?
What was the most important diagnostic change?
Keep reading
More from the Scripta blog
MeetingsWhen you pause a meeting recording, it should actually stop
A real pause stops capture, excludes the break from recorded duration, and respects the reason someone reached for the control.
ProductWhy meeting transcripts should not guess speaker names
Diarization can separate voices without knowing who they belong to. “Speaker 1” is safer than attaching a plausible but unverified name.
ProductWhy Scripta does not show a live transcript while you record
No words appear during a Scripta recording because audio is not sent for transcription mid-meeting. Here is what happens before and after Finish.
ProductA file without meeting context is just an attachment
The document is easy to download and hard to understand. Keep the meeting, decision and next action attached so teammates know why the file matters.