cloudflare / cloudflare/realtimekit-ui

Post-meeting transcription never runs: transcribe_on_end yields 0 transcription minutes and a 0-byte transcript on every session

Open
#164 2 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
64
Forks
22
Avg merge
1d 18h
Merged PRs (30d)
5

Description

## Summary

With `transcribe_on_end: true` set on the meeting and a recording that completes
and uploads successfully, post-meeting transcription **never executes**. Every
session on our app returns `transcription_minutes_consumed: 0` and a **0-byte**
transcript.

This is not a delay or a partial result — the work never starts.

The transcript endpoint returns a **non-session-scoped** download URL that is
identical for every session on the account:

```
https://.r2.cloudflarestorage.com/rtk-prod-transcription/store/default/transcript.csv
```

Note `store/default/transcript.csv` — the same path regardless of `session_id`.
That suggests the per-session transcript object is never written, and the
endpoint falls back to an empty default.

## Environment

| | |
|---|---|
| `@cloudflare/realtimekit` | 2.0.1 |
| `@cloudflare/realtimekit-ui` | 2.0.1 |
| `@cloudflare/realtimekit-react-ui` | 2.0.1 |
| App ID | `8045350b-e530-4bf4-aa36-1587f8a489b3` |
| Workers plan | **Paid** (no Neuron ceiling) |
| Recording storage | custom `storage_config` → our own R2 bucket |

Account ID available on request.

## Steps to reproduce

1. Create a meeting with post-meeting transcription enabled:

```json
POST /accounts/{account}/realtime/kit/{app}/meetings
{
"title": "…",
"record_on_start": false,
"transcribe_on_end": true,
"summarize_on_end": false,
"ai_config": { "transcription": { "language": "en" } }
}
```

2. Two participants join with distinct `custom_participant_id`s and distinct
presets, and both speak.
3. Start a recording (`POST /recordings`) with `video_config.export_file` and
`audio_config.export_file` both true.
4. Both participants leave. The recording reaches `UPLOADED`.
5. `GET /sessions/{session_id}/transcript` → 0 bytes, indefinitely.

## Expected

A transcript is generated from the participant audio after the meeting ends, and
delivered via the `meeting.transcript` webhook or the transcript REST endpoint.

## Actual

- `transcription_minutes_consumed: 0`
- `GET /sessions/{id}/transcript` returns a URL that downloads **0 bytes**
- No `meeting.transcript` webhook is ever delivered (the webhook is registered
and subscribed to that event; `recording.statusUpdate` and
`meeting.participantJoined` from the same webhook arrive reliably)
- `POST /sessions/{id}/summary` returns `{"status":"queued"}` and then produces
nothing, consistent with no transcript existing

## Evidence

Latest session, with every precondition verified before and after:

```
session 3771391f-fa65-49a7-aaef-1015b98af9de
status ENDED
total_participants 2
recording_status UPLOADED
transcription_minutes_consumed 0
settings {"record_on_start":false,"live_stream_on_start":false,
"summarize_on_end":false,"transcribe_on_end":true}

participants:
- Test Client [orykl_customer] cpi=cms8yyq0l0007dtxdkm6l2sgk
- Nick Bester [orykl_practitioner] cpi=cmqr1hrw70000ldx5vcnnzruy

GET /sessions/3771391f-…/transcript -> 0 bytes
```

Affected sessions (all `recording_status: UPLOADED`, all 0-byte transcripts):

```
3771391f-fa65-49a7-aaef-1015b98af9de 2026-08-05
0f5322f3-b774-4f72-aeb8-91ae63a126f5 2026-08-05
9fb67abc-46e8-444b-85f8-890998d6ba0d 2026-08-04
0a20aa62-afc0-4433-903a-1af9de748010 2026-08-03
41ec51b9-c862-416e-a477-f99bb09ea604 2026-07-31
299831b3-fde5-468a-9a40-8cc536539861 2026-07-31
```

The oldest is 31 July, so this is not a recent regression on our side.

## What we ruled out

| Hypothesis | Ruled out by |
|---|---|
| Workers AI Neuron budget exhausted | Workers **Paid** plan; no ceiling, usage simply bills |
| Custom `storage_config` deprives transcription of audio | The recording object still carries both `download_url` and `audio_download_url` in RealtimeKit-managed storage |
| Real-time transcription interfering | Reproduced with preset `transcription_enabled` both **true** and **false**; `transcription_minutes_consumed: 0` in the latter |
| Invalid `ai_config.transcription.language` | `"en"` is accepted and echoed back on the meeting object |
| Recording never happened / no audio | `recording_status: UPLOADED`, 17 MB MP4 + 6.5 MB M4A, verified to contain speech from both participants |
| Participants collapsed onto one identity | Verified two distinct `custom_participant_id`s, `user_id`s and presets |
| Insufficient time | Empty 8 minutes after; a prior session still empty **24 hours** later |

## Additional observations

**1. The `format` query parameter is ignored.**
`GET /sessions/{id}/transcript?format=SRT|VTT|JSON|CSV` returns the same
`…/store/default/transcript.csv` URL in all four cases.

**2. `total_participants` under-reports when two peers share a
`custom_participant_id`.** In an earlier session two peers joined with the same
`custom_participant_id`; `max_concurrent_participants` correctly showed `2`
while `total_participants` showed `1` and the participants list contained only
one entry. This is not the subject of this report, but it made the transcription
issue harder to diagnose and may be worth separating.

## Question

Is post-meeting transcription expected to work when the recording uses a custom
`storage_config`? The transcription docs state that transcription uses
RealtimeKit-managed storage independently, and suggest custom-storage users
"run your own transcription pipeline" instead. If managed transcription is not
supported alongside `storage_config`, we would expect an error rather than a
silently empty transcript — and it would be worth making that explicit in the
docs.

Relatedly: is `storage_config.type: "cloudflare"` supported? It is not in the
documented enum (`aws | azure | digitalocean | gcs | sftp`) but works correctly
for recordings against R2.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the documented POST /meetings, POST /recordings, GET /sessions/{session_id}/transcript, and meeting.transcript webhook flow with a custom storage_config. Check the transcription and storage documentation, including the supported storage_config.type values. Done means establishing whether managed post-meeting transcription is supported, and documenting the behavior or returning a clear error instead of an empty transcript.

Written by the indexing model from the issue text.

Assessment

Domain
api, backend, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.