Why won't Instagram transcribe my video?
Nearly every "it won't transcribe" is one of five failures, and each one leaves a different signature, so you can usually identify yours before you change a single setting. Nothing was said in the clip — or the file carries no audio track at all, which fails earlier and in a different place. There is sound, but it is music rather than speech. There is speech, but the language picker is set to a language the model was not told to expect. The clip changes language part-way through, and one setting has to cover the whole file. Or the video cannot be reached at all, because the post is gone, the account is private, or you never held a file to begin with — and this page has no route to a post: its script contains exactly one fetch( and one import(, both aimed at the model and the library that runs it, and zero FormData, XMLHttpRequest, sendBeacon or POST. One thing all five share is cost: a three-minute clip holds the same 80.64 MB in the tab whether it turns out to be a music bed or an interview, because the audio is decoded before recognition starts and the decode has no idea what is in it.
- Nothing was said. Either the file has no audio track at all — a screen recording made with the system audio muted, a WebM carrying a picture and nothing else — or it has a track that is empty of speech. The first case never reaches the model: the browser refuses the decode. The second reaches it and comes back headed "No speech found" rather than "Transcript". A file that is merely too long for the tab fails with the same message as a file with no audio in it, and a 20-minute clip would have needed 537.6 MB held at once.
- There is sound, but it is music. Whisper does not answer silence with an empty string; it answers with an annotation, which is a truthy string and therefore passes any test for "did we get text back". A clip carrying a music bed and no speech was measured coming back headed "Transcript" with "2 words", the two words being "(smooth music)". The decode that produced that answer had already cost 80.64 MB for the three-minute file it was taken from.
- The language picker is set wrong. The picker's list is not typed into the page; it is read from the model's own
generation_config.json— 3,832 bytes declaring 99 language tokens for the default checkpoint. Set it to a language other than the one being spoken and the words come back wrong or not at all, and nothing in the result tells you that is what happened. - The clip changes language part-way. One
languagevalue is passed for the entire run, while the audio is cut into 30-second slices of 1.92 MB each at 16,000 Hz mono. Every slice is recognised under that single setting, so a Reel that opens in one language and switches mid-way gets one language for both halves. - The video cannot be reached at all. Deleted, private, or never downloaded. Measured logged-out, a Reel URL returns HTTP 200 and 629,715 bytes of HTML — and a fabricated ID that has never existed returns exactly the same 629,715 bytes, with zero
<video>elements and zero occurrences ofvideo_url, so the response carries no information about whether the post exists.
The five signatures in one picture
generation_config.json declaring 99 language tokens, and the response that ends question five is 629,715 bytes whether or not the post exists.Failure 1: nothing was said — and the two ways that fails
This is the failure people are most confident is not theirs, because the file plays perfectly on their machine. It splits into two cases that fail in different places. If the file has no audio track at all — the commonest cause is a screen recording made while the system output was muted, or a WebM exported with a picture and no sound — the failure happens before any model is loaded, because the browser will not decode what is not there. If the file has an audio track that simply contains no speech, the decode succeeds, the model runs, and the result panel is headed "No speech found" instead of "Transcript".
The measurement worth knowing here is that the browser does not tell these causes apart, and it does not tell them apart from a third one either. Measured in an ordinary browser tab on this machine, a WebM with a picture and no audio track, a 77-byte text file renamed to .mp4, and a valid 20-minute WAV all reject the decode with the same error: EncodingError, "Unable to decode audio data", code 0, with nothing in the error object that separates them. The third of those is not damage and not silence — it is size: the same 20-minute file, held as 48,000 Hz stereo 32-bit float, is 460.8 MB before anything is resampled, and 537.6 MB once the 16,000 Hz mono copy exists alongside it.
Because the browser will not say which of the three it was, the honest move is to check them in the cheapest order. Play the file and confirm there is sound in it. Then try a much shorter clip cut from the same recording: if the short one transcribes, the file was fine and the limit was memory; if the short one fails identically, the audio is missing or in a codec the browser does not have.
Failure 2: there is sound, but it is music rather than speech
A Reel built around a music bed is the case that used to look like a bug rather than a failure. Whisper does not return an empty string when it finds no speech; it returns an annotation, and an annotation is a truthy string, so a plain test for "did we get any text" passes and the page reports success. Measured here: a clip carrying a music bed and no speech at all came back headed "Transcript" with "2 words", the two words being "(smooth music)".
The annotations arrive in two families, and the page has to strip both. Square brackets produce things like [BLANK_AUDIO], [Music] and [Applause]. Round brackets produce things like (smooth music), (upbeat music) and (birds chirping) — and the round-bracket family is what a music bed actually comes back as, which is why knowing only about the square one was not enough. Asterisks are deliberately left alone: *music* is not an annotation, and a transcript is allowed to contain an asterisk. Once both families are stripped, a music-only file reports "No speech found" and says why.
None of this saves memory, and it is worth being clear about that. A three-minute music bed costs the tab exactly what a three-minute interview costs — 69.12 MB of decoded 48,000 Hz stereo plus 11.52 MB of resampled 16,000 Hz mono, 80.64 MB in total — because the decode runs to completion before the model is given anything to recognise.
Failure 3: the language picker is set to the wrong language
The language picker on this page is not a hand-written list, and that matters to this failure. It is read from the model's own generation_config.json — the file the model itself uses to decide which language tokens exist — so the picker lists exactly the languages the loaded checkpoint accepts and cannot drift away from the model if the checkpoint is ever changed. Measured on this machine: the default checkpoint's file is 3,832 bytes and declares 99 language tokens; the smaller checkpoint's file is 3,772 bytes and declares the same 99. The tokens themselves look like <|en|> and <|zh|>, and the display names beside them come from the browser's own locale data rather than a second hand-written table.
What makes this failure hard to spot is that it produces no error. The model still runs, the panel is still headed "Transcript", and the output is either fluent text in a language nobody in the video was speaking, or a short string of misheard words. The run takes about as long and costs about as much as a correct one: the same 1.92 MB per 30-second slice, the same 26.88 MB for a one-minute clip. Nothing about the result tells you that the input was wrong rather than the audio.
The fix is to match the picker to what you hear rather than to what you expect, and to re-run once with the corrected value instead of editing the output. If you do not know what language the clip is in, the picker's full list of 99 tokens is the place to look — it is the model's own inventory, not a marketing claim about coverage.
Failure 4: the clip changes language part-way through
One language value is passed for the whole run. The call the page makes is language: lang together with task: 'transcribe', return_timestamps: true and chunk_length_s: 30 — a single language, applied to a file that has been cut into 30-second slices. At 16,000 Hz mono, one slice is 480,000 samples, which is 1.92 MB; a three-minute Reel is six such slices, and all six are recognised under the same setting.
language value is passed for the whole run — the call carries language: lang together with task: 'transcribe', return_timestamps: true and chunk_length_s: 30 — so the last three boxes are recognised as if they were the language in the first three. Nothing in the page detects the switch, because the language is a parameter rather than something inferred per slice. Cutting the same clip into two 90-second files makes each half its own run with its own setting, and it also drops the memory a run has to hold from 80.64 MB to 40.32 MB; the two transcripts join afterwards because each run returns timestamps measured from the start of the file it was given.So a Reel that opens in one language and switches to another halfway does not get a mixed transcript — it gets whichever single language was selected, applied to both halves. Nothing in the page detects the switch, because the language is a parameter you set rather than something inferred per slice. This is also why a wrong guess on this failure looks exactly like Failure 3: no error, a clean panel, and text that is wrong in a way the page cannot see.
The workable route is to split the audio at the point where the language changes and run each half with its own setting. Cutting a three-minute clip into two ninety-second files keeps the memory per run down as well — each half holds 40.32 MB rather than the 80.64 MB the whole file costs — and the two transcripts can be joined afterwards, because each run returns timestamps measured from the start of the file it was given.
Failure 5: the video cannot be reached at all
The last failure is not about the audio, and it is the one this page cannot fix. If the post has been deleted, if the account is private, or if you never had the file in the first place, there is no route from here to those words. That is structural rather than a limitation of the model. The script that does the work is 22,349 bytes and contains exactly one fetch( and exactly one import(, aimed at the model's language list and the recognition library respectively; it contains zero FormData, zero XMLHttpRequest, zero sendBeacon and zero POST requests, and the 16 deployable files of this site contain zero <form> elements in 190,398 bytes. A page that cannot construct a request has no route by which a post could be fetched, so "just give it the link" is not a gap that could be closed by trying harder.
It is worth seeing what Instagram actually returns, because it explains why the link route is not merely restricted but empty. Measured on 12 October 2026 with a logged-out request and a Chrome user-agent: a Reel URL returns HTTP 200 and 629,715 bytes of HTML. A fabricated ID that has never existed returns exactly the same 629,715 bytes — the same 79 script tags, the same zero <video> elements, the same zero occurrences of video_url, and identical counts of the markers login (19), Login (4), signup (6) and logged_out (4). Byte for byte, the response does not depend on whether the post exists, which means a tool that reads that response cannot tell a deleted Reel from a live one either.
What is left is the file. If the Reel is yours, it is in your own export or your camera roll. If it is not yours and the post still exists, downloading it is the step that has to happen somewhere other than here. If the post is gone, nothing on this page can recover it, and any tool that claims otherwise is describing a route that does not exist.
How to tell which of the five you have
- Play the file once and listen. No sound at all points at Failure 1's first case; only music points at Failure 2; speech you cannot quite follow in the language you expected points at Failure 3.
- Read the heading of the result panel. "No speech found" means the decode succeeded and the model found nothing it would call words. A decode error means the browser refused the file, which is Failure 1's first case or a file too large for the tab — 537.6 MB of decoded audio is the ceiling a 20-minute clip would ask for.
- Check the picker against what you actually hear, not against the language you assumed. This is the only one of the five that produces a confident, fluent, wrong answer, and the only one where re-running with one setting changed is the whole fix.
- Listen for a language change inside the clip. If the speaker switches part-way, split the audio there and run each part with its own setting rather than trying to make one run cover both.
- Confirm you hold the file. If the only thing you have is a link, stop here: with zero request-constructing primitives in the script, no setting on this page can turn a post into audio, deleted or otherwise.
What the error message can and cannot tell you
The error text on this page names three causes rather than one, and that is deliberate rather than vague. Because the browser reports a missing audio track, a codec it cannot read and a file too large for the tab with the identical EncodingError and code 0, naming only one of them would be a false statement about the other two — the page used to tell someone with a silent screen recording that their file was damaged or too long. Naming all three, and then telling you how to find out which it was by testing a shorter clip, is the most that the information available in the tab supports.
Everything the page can measure, it reports. The result panel gives the duration of the audio in seconds, the word count and segment count when there is speech, the time the recognition took, and which of the two checkpoints produced it. A panel that reports a duration, a word count and a segment count is telling you that the audio was found and that the model produced text — so if the text is still wrong, the setting is the place to look rather than the file. A panel headed "No speech found" is telling you the opposite, and a decode error is telling you the file never got that far. None of those three states is ambiguous, which is why the fifth failure is the only one where the answer is simply that the audio is not reachable from here.
Last checked: 12 October 2026. The counts of this site's own files were taken on this machine on that date rather than remembered: the script is 22,349 bytes, the stylesheet is 9,455 bytes, the 16 deployable files total 190,398 bytes, and the script contains one fetch(, one import(, and zero FormData, XMLHttpRequest, sendBeacon or POST. The two generation_config.json sizes — 3,832 bytes and 3,772 bytes, each declaring 99 language tokens — were fetched on the same day. The Instagram figures were measured the same day with a logged-out request and a Chrome user-agent: HTTP 200, 629,715 bytes for both a Reel URL and a fabricated ID, zero <video> elements and zero video_url occurrences in either. The audio arithmetic is derived, not measured — Web Audio fixes a decoded sample at 32 bits, so one second of 48,000 Hz stereo is 48,000 × 2 × 4 = 384,000 bytes and the same second at 16,000 Hz mono is 64,000 bytes, and both buffers exist at once while the resample runs, giving 26.88 MB for one minute, 80.64 MB for three and 537.6 MB for twenty. The "2 words" music-bed result and the three identically-reported decode failures were measured in an ordinary browser tab on a two-core, four-gigabyte machine. Figures that could not be measured or derived are absent rather than estimated.
The mechanics behind the tab-memory figures are counted on the page about whether transcribing uploads the video. The ceiling that decides whether a long clip finishes at all is on the Reel length limit page, and what Instagram's own captions do and do not produce is on the page about Instagram's built-in captions. If the Reel is one you cannot open, the private Reel page covers the routes that still work, and if you are feeding more than one file through, bulk transcribing Instagram Reels covers what changes.