You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
cohere_asr silently returns a partial transcript once input passes roughly 50 s. A 134 s
recording yields 75 words out of about 279, so 27% of the content, with the opening spliced
directly to the closing clause. The run returns success, nothing is logged, and transcribe_was_truncated() reads false, so a consumer cannot detect it. Meanwhile max_audio_ms advertises 400 s.
Found while using Handy for dictation. Handy is not implicated: calling the library directly
through the Python binding reproduces it exactly.
Environment
transcribe-cpp transcribe.dll reporting transcribe_version() == 0.1.3, as shipped in
Handy 0.9.0 (Windows x86_64)
Python binding transcribe_cpp-0.1.3-py3-none-any.whl via TRANSCRIBE_LIBRARY
Windows 11 Pro (10.0.26200), RTX 5090 (Vulkan0), Ryzen 9 9900X
Input: 134.01 s, pcm_s16le, 16 kHz mono, one continuous English reading of about 279 words
What the library reports about itself
max_audio_ms=400000 (400.0 s)
effective_max_audio_ms=400000 per session, so n_ctx does not narrow it
max_timestamp_kind=none
supports_streaming=False
transcribe_was_truncated() == false on the 134 s run that loses 73% of the content
was_truncated is also false on a 20 s prefix, which transcribes correctly, so the flag does
not distinguish the two cases at all.
Either a transcript covering the input, or a refusal. The library declares a 400 s ceiling and
defines InputTooLong and OutputTruncated, so 134 s should either transcribe or report one.
Actual
75 of about 279 words, one segment, no error, no warning, was_truncated false. The transcript
covers the opening then jumps to the passage's final clause:
Alright, this is the transcription test for Cohere. Bug 2047421 tracks a dark mode white
flash on Windows. The repro harness pointed at the pre Zool skeleton UI and a DWM first show
cloak race, and the component moved to Mozglue afterward. Most mornings start and about
preferences. The settings redesign lives behind browser.settings-redesign.enabled, and the
pane mapping shim in bug.Bug 204742195 shows up often enough that a transcript should not
mangle any of them.
The bolded seam fuses a fragment of the passage's first bug number to a fragment of a later one.
Length sweep
Same recording truncated to increasing prefixes, model loaded once, only input duration
varying. Expected counts use the measured reading rate of 2.08 words per second.
input
words returned
expected
ratio
20.0 s
42
42
101%
30.0 s
59
62
94%
35.0 s
68
73
93%
40.0 s
74
83
89%
50.0 s
97
104
93%
60.0 s
94
125
75%
90.0 s
81
187
43%
134.0 s
75
279
27%
Faithful through 50 s, degrading from 60 s, so the usable ceiling is about an eighth of the
advertised 400 s. Past that the word count does not grow with input, it saturates near 90 and
then decreases, which is what puts the loss in the middle rather than the tail.
No chunking happens on this path
len(res.segments) == 1, max_timestamp_kind is none, supports_streaming is False, so
the whole clip goes through a single pass. The KV cache line shows the encoder does see all of
it:
cohere kv_cache: allocated 84.4 MB (f16) (self: 1024 session x 8 layers, cross: 1676 T_enc x 8 layers)
1676 encoder frames covers 134 s at 8x subsampling of 10 ms mel frames. The 8x is inferred
from that arithmetic rather than read from the model file. So the encoder consumes everything
and the decoder fails to traverse it.
The CPU backend makes the decoder's role clearer. On identical input it does not stop early, it
degenerates:
...the pane mapping shim in bug 20474214222222222222222222222222222222 (continues for 250
characters)
A repetition loop and an early jump to the ending are two symptoms of one condition, a decoder
attending over far more encoder context than it was trained on. That suggests an input-length
policy problem in the cohere_asr family implementation rather than any one compute kernel.
For comparison, the same file posted to https://api.cohere.com/v2/audio/transcriptions with model=cohere-transcribe-03-2026 returns the complete passage, 298 words, and its output
carries four visible seams where segments were joined. The hosted path chunks. This one does
not. The weights are fine.
The decisive control: chunk the input and the same library succeeds
Splitting the identical recording into four pieces under 45 s, cutting inside natural silences,
and concatenating gives 294 words against the 279 word reference. All eight paragraphs present.
Same transcribe.dll, same GGUF, same Vulkan0 device, same process. Boundaries at 36.82 s,
80.95 s, and 123.14 s.
run
words
vs reference
Reference passage
279
100%
Hosted api.cohere.com
298
107%
Local, 4 chunks under 45 s
294
105%
Local, one 134 s pass
75
27%
A caller that chunks gets a complete transcript from the library that fails in one pass.
Suggested fix, in preference order
Chunk internally for cohere_asr, matching the reference implementation. The model card for CohereLabs/cohere-transcribe-03-2026 puts this in the processor: audio exceeding the
feature extractor's max_audio_clip_s is split and reassembled via audio_chunk_index,
demonstrated on a 55 minute input.
Failing that, lower max_audio_ms for this architecture to a length that works, so long
input is rejected with InputTooLong rather than silently answered.
Audio integrity. Per-second RMS shows continuous speech energy from 3 s to 133 s, and silencedetect at -40 dB finds no gap of 1.5 s or longer.
Microphone gating is not the cause. The file holds 17.8 s of exact digital zero across 30
gaps of 200 ms or more, which is a hardware noise gate zeroing pauses, since energy ramps
down into each gap rather than cutting mid-waveform.
Different model, same length. Parakeet Unified EN 0.6B transcribed a 140.5 s reading of
the same passage end to end.
CUDA not tested. The cuda lane of v0.1.3 loads ggml-cuda.dll but registers no device
here, reporting cohere: cuda backend requested but not available then failing model load
with BackendError status 8, because the archive ships no CUDA runtime and this machine has
the driver without the CUDA 12 toolkit. Vulkan and CPU already agree, so this seemed unlikely
to add anything.
Related
#89, silent tail truncation in the whisper short-form path via an early EOS at a prosodic
pause, closed by #96. Same observable shape: success status, transcribe_was_truncated()
false, content missing. Different trigger, since that one needed an initial_prompt and short
audio while this needs no prompt and long audio. Whether they share a decode-loop termination
path is for you to say.
Audio
The 134 s WAV used for every number above is attached in a comment below.
Summary
cohere_asrsilently returns a partial transcript once input passes roughly 50 s. A 134 srecording yields 75 words out of about 279, so 27% of the content, with the opening spliced
directly to the closing clause. The run returns success, nothing is logged, and
transcribe_was_truncated()reads false, so a consumer cannot detect it. Meanwhilemax_audio_msadvertises 400 s.Found while using Handy for dictation. Handy is not implicated: calling the library directly
through the Python binding reproduces it exactly.
Environment
transcribe.dllreportingtranscribe_version() == 0.1.3, as shipped inHandy 0.9.0 (Windows x86_64)
transcribe_cpp-0.1.3-py3-none-any.whlviaTRANSCRIBE_LIBRARYhandy-computer/cohere-transcribe-03-2026-gguf,cohere-transcribe-03-2026-Q5_K_M.ggufarch=cohere_asr,variant=cohere-transcribe-03-2026pcm_s16le, 16 kHz mono, one continuous English reading of about 279 wordsWhat the library reports about itself
was_truncatedis also false on a 20 s prefix, which transcribes correctly, so the flag doesnot distinguish the two cases at all.
Steps to reproduce
Expected
Either a transcript covering the input, or a refusal. The library declares a 400 s ceiling and
defines
InputTooLongandOutputTruncated, so 134 s should either transcribe or report one.Actual
75 of about 279 words, one segment, no error, no warning,
was_truncatedfalse. The transcriptcovers the opening then jumps to the passage's final clause:
The bolded seam fuses a fragment of the passage's first bug number to a fragment of a later one.
Length sweep
Same recording truncated to increasing prefixes, model loaded once, only input duration
varying. Expected counts use the measured reading rate of 2.08 words per second.
Faithful through 50 s, degrading from 60 s, so the usable ceiling is about an eighth of the
advertised 400 s. Past that the word count does not grow with input, it saturates near 90 and
then decreases, which is what puts the loss in the middle rather than the tail.
No chunking happens on this path
len(res.segments) == 1,max_timestamp_kindisnone,supports_streamingisFalse, sothe whole clip goes through a single pass. The KV cache line shows the encoder does see all of
it:
1676 encoder frames covers 134 s at 8x subsampling of 10 ms mel frames. The 8x is inferred
from that arithmetic rather than read from the model file. So the encoder consumes everything
and the decoder fails to traverse it.
The CPU backend makes the decoder's role clearer. On identical input it does not stop early, it
degenerates:
A repetition loop and an early jump to the ending are two symptoms of one condition, a decoder
attending over far more encoder context than it was trained on. That suggests an input-length
policy problem in the
cohere_asrfamily implementation rather than any one compute kernel.For comparison, the same file posted to
https://api.cohere.com/v2/audio/transcriptionswithmodel=cohere-transcribe-03-2026returns the complete passage, 298 words, and its outputcarries four visible seams where segments were joined. The hosted path chunks. This one does
not. The weights are fine.
The decisive control: chunk the input and the same library succeeds
Splitting the identical recording into four pieces under 45 s, cutting inside natural silences,
and concatenating gives 294 words against the 279 word reference. All eight paragraphs present.
Same
transcribe.dll, same GGUF, same Vulkan0 device, same process. Boundaries at 36.82 s,80.95 s, and 123.14 s.
A caller that chunks gets a complete transcript from the library that fails in one pass.
Suggested fix, in preference order
cohere_asr, matching the reference implementation. The model card forCohereLabs/cohere-transcribe-03-2026puts this in the processor: audio exceeding thefeature extractor's
max_audio_clip_sis split and reassembled viaaudio_chunk_index,demonstrated on a 55 minute input.
max_audio_msfor this architecture to a length that works, so longinput is rejected with
InputTooLongrather than silently answered.transcribe_was_truncated()return true here. The signal exists and readsfalse during a real truncation, which is the gap [whisper] short-form: initial_prompt can cause silent tail truncation (early EOS at a pause) — same weights+prompt complete in whisper.cpp #89 described before fix short-form tail truncation via seek continuation #96 fixed it for
whisper.
Other controls
silencedetectat -40 dB finds no gap of 1.5 s or longer.gaps of 200 ms or more, which is a hardware noise gate zeroing pauses, since energy ramps
down into each gap rather than cutting mid-waveform.
the same passage end to end.
cudalane of v0.1.3 loadsggml-cuda.dllbut registers no devicehere, reporting
cohere: cuda backend requested but not availablethen failing model loadwith
BackendErrorstatus 8, because the archive ships no CUDA runtime and this machine hasthe driver without the CUDA 12 toolkit. Vulkan and CPU already agree, so this seemed unlikely
to add anything.
Related
#89, silent tail truncation in the whisper short-form path via an early EOS at a prosodic
pause, closed by #96. Same observable shape: success status,
transcribe_was_truncated()false, content missing. Different trigger, since that one needed an
initial_promptand shortaudio while this needs no prompt and long audio. Whether they share a decode-loop termination
path is for you to say.
Audio
The 134 s WAV used for every number above is attached in a comment below.