Bluetooth page stuck on Loading forever when bluetoothd is not running #66

Closed
opened 2026-10-13 19:33:36 +08:00 by Breadway · 3 comments
Owner

How it was found: clicking through the 98be903 build (main, v2 merged) in a headless BOS live VM on 2026-10-13. The VM has no Bluetooth adapter, and bluetooth.service is inactive. Bluetooth still said "Loading..." more than 2 minutes after opening.

Confidence: high. In the same VM, timeout 5 bluetoothctl list exits 124: bluetoothctl waits for bluetoothd forever. Home handles the same machine fine ("No adapter").

Bug: src/src/commands/bluetooth.rs runs every bluetoothctl call (get_adapter_powered show, run_devices, info, power) through Command::output().await with no timeout. When bluetoothd is not running (service disabled, or no adapter so the service never starts), the first call never returns, and the page never leaves its loading state. Any user who has disabled the Bluetooth service sees this.

Fix: wrap bluetoothctl calls in a short tokio timeout (or check systemctl is-active bluetooth first), treat a timeout as "no adapter / Bluetooth service off", and show that state with a way to start the service.

**How it was found:** clicking through the 98be903 build (main, v2 merged) in a headless BOS live VM on 2026-10-13. The VM has no Bluetooth adapter, and bluetooth.service is inactive. Bluetooth still said "Loading..." more than 2 minutes after opening. **Confidence:** high. In the same VM, `timeout 5 bluetoothctl list` exits 124: bluetoothctl waits for bluetoothd forever. Home handles the same machine fine ("No adapter"). **Bug:** src/src/commands/bluetooth.rs runs every bluetoothctl call (`get_adapter_powered` `show`, `run_devices`, `info`, `power`) through `Command::output().await` with no timeout. When bluetoothd is not running (service disabled, or no adapter so the service never starts), the first call never returns, and the page never leaves its loading state. Any user who has disabled the Bluetooth service sees this. **Fix:** wrap bluetoothctl calls in a short tokio timeout (or check `systemctl is-active bluetooth` first), treat a timeout as "no adapter / Bluetooth service off", and show that state with a way to start the service.
Author
Owner

PR: #77 (head 40a9ec5)

PR: https://git.breadway.dev/Breadway/bos-settings/pulls/77 (head 40a9ec5)
Author
Owner

Review PASS for PR #77, exact head 40a9ec5ae9 (base main 14d1315, mergeable).

Criteria:

  • All bluetoothctl calls bounded, child killed: pass. btctl()/bounded() in src/src/commands/bluetooth.rs wrap every call in tokio timeout with kill_on_drop (5s quick, 15s scan, 30s pair/connect). Test hung_command_times_out_and_is_killed (sleep 30 vs 200ms) passes.
  • Stopped service or timeout treated as no adapter / service off, page leaves Loading: pass. get_bluetooth_state returns no-adapter, service-off or ready; Bluetooth.svelte load() sets powered and serviceOff. Coordinator VM evidence: "No Bluetooth adapter found" immediately.
  • Service-off offers a way to start: pass. "Start Bluetooth" button calls start_bluetooth_service, then reloads; errors shown.
  • No capability regressions: pass. pair/connect/disconnect/forget/scan/power keep their logic with timeouts; Home still uses get_adapter_powered, now bounded.
  • Security: pass. start_bluetooth_service takes no arguments and runs only pkexec systemctl start bluetooth.service; the new command is not a generic runner.

Checks run on the exact head in a scratch worktree: cargo fmt --check ok; cargo clippy --locked --all-targets -D warnings ok; cargo test --locked 147 passed; npm ci, npm run check (0 errors, 0 warnings), npm test, npm run build all exit 0.
CI: Forgejo status "check / check (push)" success on 40a9ec5. No reviews or change requests on the PR.

Non-blocking notes: systemctl is-active has no explicit timeout (it does not wait on bluetoothd). The pair timeout kills bluetoothctl at 30s, which is acceptable. No test covers get_bluetooth_state branches directly.

Not merged: the reviewer session has no Forgejo PR or merge tools, so the PR is awaiting merge by the coordinator.

Review PASS for PR #77, exact head 40a9ec5ae9950076d689972d94dd27d01221ebf9 (base main 14d1315, mergeable). Criteria: - All bluetoothctl calls bounded, child killed: pass. btctl()/bounded() in src/src/commands/bluetooth.rs wrap every call in tokio timeout with kill_on_drop (5s quick, 15s scan, 30s pair/connect). Test hung_command_times_out_and_is_killed (sleep 30 vs 200ms) passes. - Stopped service or timeout treated as no adapter / service off, page leaves Loading: pass. get_bluetooth_state returns no-adapter, service-off or ready; Bluetooth.svelte load() sets powered and serviceOff. Coordinator VM evidence: "No Bluetooth adapter found" immediately. - Service-off offers a way to start: pass. "Start Bluetooth" button calls start_bluetooth_service, then reloads; errors shown. - No capability regressions: pass. pair/connect/disconnect/forget/scan/power keep their logic with timeouts; Home still uses get_adapter_powered, now bounded. - Security: pass. start_bluetooth_service takes no arguments and runs only pkexec systemctl start bluetooth.service; the new command is not a generic runner. Checks run on the exact head in a scratch worktree: cargo fmt --check ok; cargo clippy --locked --all-targets -D warnings ok; cargo test --locked 147 passed; npm ci, npm run check (0 errors, 0 warnings), npm test, npm run build all exit 0. CI: Forgejo status "check / check (push)" success on 40a9ec5. No reviews or change requests on the PR. Non-blocking notes: systemctl is-active has no explicit timeout (it does not wait on bluetoothd). The pair timeout kills bluetoothctl at 30s, which is acceptable. No test covers get_bluetooth_state branches directly. Not merged: the reviewer session has no Forgejo PR or merge tools, so the PR is awaiting merge by the coordinator.
Author
Owner

Verified on integrated main a7b3e8b (merge a7b3e8b, PR #77): hung_command_times_out_and_is_killed, adapter sysfs test, 179 cargo tests, svelte-check/npm test/build pass. Live VM (no adapter, bluetooth.service inactive): page shows No Bluetooth adapter found immediately instead of hanging.

Verified on integrated main a7b3e8b (merge a7b3e8b, PR #77): hung_command_times_out_and_is_killed, adapter sysfs test, 179 cargo tests, svelte-check/npm test/build pass. Live VM (no adapter, bluetooth.service inactive): page shows No Bluetooth adapter found immediately instead of hanging.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Breadway/bos-settings#66
No description provided.