Fix API key race: wait inside ensureBridgeStarted, not callers#6014
Conversation
The previous fix added waitForKeys() in OnboardingChatView before sendMessage(), but the bridge was already started earlier by ViewModelContainer.warmupBridge() → ensureBridgeStarted(). By the time our wait ran, the bridge was alive in Mode B (no key). Fix: move the wait into ensureBridgeStarted() itself — one place, covers all callers (warmup, sendMessage, anything). Also store the fetchTask from OmiApp launch so waitForKeys() can always await it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR consolidates the Changes:
Issue found:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant OmiApp
participant APIKeyService
participant ChatProvider
participant ACPBridge
OmiApp->>APIKeyService: fetchTask = Task { fetchKeys() }
Note over APIKeyService: fetchTask stored for awaiting
OmiApp->>ChatProvider: warmupBridge() [via ViewModelContainer]
ChatProvider->>ChatProvider: ensureBridgeStarted()
alt acpBridge.passApiKey == true
ChatProvider->>APIKeyService: waitForKeys()
APIKeyService-->>ChatProvider: (keys loaded)
end
ChatProvider->>ACPBridge: start()
Note over ACPBridge: Reads ANTHROPIC_API_KEY<br/>→ launches in Mode A
OmiApp->>ChatProvider: sendMessage(...)
ChatProvider->>ChatProvider: ensureBridgeStarted()
Note over ChatProvider: guard !acpBridgeStarted → returns true immediately
|
…Hardware#6014) ## Summary Previous fix (BasedHardware#5987) added `waitForKeys()` in OnboardingChatView before `sendMessage()`, but the bridge was already started by `ViewModelContainer.warmupBridge()` → `ensureBridgeStarted()` — 26ms before keys loaded. The bridge launched in Mode B (no API key) every time. **Root cause**: `ensureBridgeStarted()` calls `acpBridge.start()` which spawns a subprocess that reads `ANTHROPIC_API_KEY` from environment. If the key isn't set yet, it starts in OAuth mode — which fails for new users who don't have a Claude account. **Fix**: Move `waitForKeys()` into `ensureBridgeStarted()` itself, right before `acpBridge.start()`. One place, covers all callers. Also store `fetchTask` from OmiApp launch so `waitForKeys()` can always await it. ## Test plan - [ ] Clean install → sign in → verify bridge starts in Mode A (check log for "Mode A (Omi API key)") - [ ] Verify onboarding chat works without Claude OAuth session 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
Previous fix (#5987) added
waitForKeys()in OnboardingChatView beforesendMessage(), but the bridge was already started byViewModelContainer.warmupBridge()→ensureBridgeStarted()— 26ms before keys loaded. The bridge launched in Mode B (no API key) every time.Root cause:
ensureBridgeStarted()callsacpBridge.start()which spawns a subprocess that readsANTHROPIC_API_KEYfrom environment. If the key isn't set yet, it starts in OAuth mode — which fails for new users who don't have a Claude account.Fix: Move
waitForKeys()intoensureBridgeStarted()itself, right beforeacpBridge.start(). One place, covers all callers. Also storefetchTaskfrom OmiApp launch sowaitForKeys()can always await it.Test plan
🤖 Generated with Claude Code