All News
TrendingAndroid

OpenClaw's Android App Has Been Silently Crashing. Two Race Conditions Nobody Caught.

Two concurrent cleanup paths could release the same MediaPlayer. A cancelled coroutine could resume a dead continuation. Both crashed the Android app with no user-facing error. Both were known concurrency patterns. One had already been fixed — in a class that got deleted.

March 22, 20265 min read
PR #52310

MediaPlayer Double-Release

Contributor: Kaneki-x · Merged by obviyus

The bug: TalkModeManager.cleanupPlayer() used a plain nullable var player. Two concurrent callers — a coroutine finally block and a UI-triggered stopSpeaking call — could both attempt stop()/ release() on the same instance.

The fix: Promote to AtomicReference<MediaPlayer?> so getAndSet(null) atomically transfers ownership. Only one cleanup path ever holds the reference.

This exact pattern was already fixed in PR #45815 — in the deleted ElevenLabsStreamingTts class. The replacement code reintroduced the same race.

PR #52318

Location Callback Race

Contributor: Kaneki-x · Merged by obviyus

The bug: LocationCaptureManager.requestCurrent() used withTimeout() around a suspended coroutine. If the timeout fired before the OS location callback arrived, the continuation was already cancelled. The callback then called cont.resume() on a dead continuation — “Already resumed” — instant crash.

The fix: Swap to the cancellation-safe overload: cont.resume(location) { _, _, _ -> }. Delayed callbacks are silently discarded instead of crashing.

This is a resubmission of PR #45821, which was closed because the contributor had “excessive open PRs.” A crash fix was closed for housekeeping reasons.

Android Users Are Second-Class Citizens

I'm going to say the thing that OpenClaw's core team won't say: the Android app is an afterthought. Not because the code is bad — it isn't. These are well-scoped, well-tested fixes. But the pattern they reveal is damning.

The MediaPlayer race condition was a known bug pattern. It was documented. It was fixed once before, in ElevenLabsStreamingTts. Then that class was deleted as part of a refactor, and the replacement — TalkModeManager — was built with the exact same concurrency bug. Nobody looked at the previous fix before writing the new code. Nobody searched for AtomicReference in the git history. They just rebuilt the problem from scratch.

The location callback race is even more basic. This is Kotlin Coroutines 101: when you use withTimeout with suspendCancellableCoroutine, you must handle the case where the callback arrives after cancellation. Every coroutines tutorial covers this. The fix is literally ten characters.

“The crash fix was closed because the contributor had too many open PRs. Not because the code was wrong. Not because the fix was disputed. Because of a queue management policy.”

The Contributor Story

Both fixes come from Kaneki-x, who first submitted them as PRs #45815 and #45821. Those were closed when the project decided the contributor had too many open pull requests. Let me emphasize this: a crash fix that affected every Android user who used TTS or location services was closed not on technical grounds, but on PR queue policy.

Kaneki-x resubmitted both patches in March 2026. They incorporated feedback from the Greptile code review bot, fixed indentation issues, and got them merged by obviyus. The second submission was cleaner. The crash had continued to affect users for the entire intervening period.

Open-source projects need contribution policies. Queue limits make sense in theory — they prevent review bottlenecks and keep the backlog manageable. But when your queue policy results in shipping known crash bugs for months, the policy is wrong. Severity should always override queue position.

What This Says About the Review Process

The security reviewers caught a legitimate concern in the MediaPlayer fix: a potential resource leak when cleanupPlayer(expectedPlayer) returns early because the reference was already swapped. If concurrent playGatewaySpeech() calls create a new player while cleanup is in progress, the old one leaks. It's a valid finding that warrants a follow-up.

The review process itself was good. The automated analysis was useful. The merge was clean. But good review on a fix that should have been prevented by institutional knowledge is still a net negative. You shouldn't need to review a concurrency fix for a pattern your own codebase already solved.

Both fixes are live for DeployClaw users running the Android app. If you've been experiencing random crashes during TTS playback or location-based commands, this is why. It was never your device. It was always the code.

DeployClaw News · Reported by Carlos Simpson

DeployClaw hosts OpenClaw instances. Upstream fixes ship automatically. This publication covers development independently.