Skip to content

fix(inference): keep a reference to the STT session.update task - #7053

Closed
Rehansanjay wants to merge 1 commit into
livekit:mainfrom
Rehansanjay:fix/inference-stt-session-update-task
Closed

fix(inference): keep a reference to the STT session.update task#7053
Rehansanjay wants to merge 1 commit into
livekit:mainfrom
Rehansanjay:fix/inference-stt-session-update-task

Conversation

@Rehansanjay

Copy link
Copy Markdown
Contributor

The bug

SpeechStream.update_options updates self._opts and then tells the server about it:

asyncio.ensure_future(self._send_session_update(update_msg))

The event loop holds only a weak reference to that future, so it can be garbage collected before the message is ever sent.

The failure is silent and one-sided. Local options say the model, language or extras changed; the server was never told; the stream keeps transcribing with the old settings. Nothing raises, nothing logs, and the caller has no way to notice.

The pending future is also never cancelled when the run loop tears the websocket down, so it can outlive the socket it was meant to write to.

The fix

Hold the task in a set that discards itself on completion, and cancel anything still pending in the run loop's finally, right next to the gracefully_cancel already there for the send/recv/vad tasks.

No behaviour change on the success path — the update is still sent in the background and update_options still returns immediately.

How it was found

ruff --select RUF006 (asyncio-dangling-task). The repo's [tool.ruff.lint] select is currently E, W, F, I, B, C4, UP, so RUF is not enabled and this rule never runs in CI.

Across livekit-agents and livekit-plugins the rule currently reports 8 findings. This PR is the only one in core; the other seven are covered by #7050 (inworld), #7051 (soniox) and #7052 (aws), plus one in livekit-plugins-hamming that sits inside _reset_runtime_for_tests and is harmless.

Worth noting my own AST scan missed this one because it only looked for create_taskensure_future has the same weak-reference semantics and ruff checks both.

If it would be useful, I am happy to send a follow-up that fixes the hamming case and adds "RUF" (or just RUF006) to the ruff select, so the whole class is caught in CI rather than one plugin at a time. Say the word and I will open it once these have landed.

Checks

ruff check, ruff check --select RUF006 and ruff format --check all pass on the changed file.

`SpeechStream.update_options` applies the new model, language or extras
to `self._opts` and then fires `asyncio.ensure_future` to tell the server
about them. The event loop only holds a weak reference to that future, so
it can be garbage collected before the message is sent.

When that happens the failure is silent and one-sided: local options say
the model changed, the server was never told, and the stream keeps
transcribing with the old settings. Nothing raises and nothing logs.

Hold the task in a set that discards on completion, and cancel whatever
is still pending when the run loop tears the websocket down, next to the
existing `gracefully_cancel` of the send/recv/vad tasks.

Caught by ruff's RUF006 (asyncio-dangling-task), which is not currently
enabled in this repo.
@Rehansanjay
Rehansanjay requested a review from a team as a code owner August 31, 2026 06:39

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@Rehansanjay

Copy link
Copy Markdown
Contributor Author

Bumping this one because it is the only finding from the sweep that is in core rather than a plugin, and its failure mode is silent.

SpeechStream.update_options sets self._opts and then fires a discarded asyncio.ensure_future to tell the server. The loop holds only a weak reference, so if that future is collected the local options say the model or language changed, the server was never told, and the stream keeps transcribing with the old settings. Nothing raises and nothing logs, so a caller has no way to notice.

Same class as #7023 and #7012, both merged. One file, +9/-1, all checks green, no open review threads.

cc @tinalenguyen since you merged #7012 and have been the most recent hand in inference/.

@Rehansanjay

Copy link
Copy Markdown
Contributor Author

Closing in favour of #7054, at @longcw's request — that PR carries this change unchanged and is being reviewed as one.

@Rehansanjay Rehansanjay closed this Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant