Skip to content

ContinuousBluetoothManager appears to accumulate keep-alive tasks after reconnect, causing concurrent commands and stuck Loops #2494

Description

@wangzb6

Describe the bug

When using DanaKit in Continuous Bluetooth mode, repeated disconnect/reconnect cycles appear to accumulate multiple concurrent keep-alive background tasks.

After several reconnect cycles, multiple keepConnectionAlive() calls begin occurring at exactly the same timestamp.

These calls then produce repeated:

A command is already running

errors, followed by disconnect timeouts, connection loss, automatic reconnects, pump synchronization failures, and eventually failed/stuck Loop cycles.

In my case, Loop later experienced an extended period where normal closed-loop operation could not continue.


Strong pattern observed in the logs

The most interesting observation is that the number of simultaneous keepConnectionAlive() calls appears to increase after every reconnect cycle.

Observed sequence:

Time Simultaneous keep-alive calls
00:04:59 2
00:07:11 3
00:09:19 4
00:11:29 5
00:13:37 6

This looks like an old background keep-alive task may remain alive after reconnect, while a new task is created.

The resulting pattern is approximately:

disconnect → reconnect → +1 keep-alive task → disconnect → reconnect → +1 keep-alive task

Eventually multiple keep-alive tasks execute at the same time.


Example log

At 00:11:29, five keep-alive calls were triggered at essentially the same time:

[2026-08-13T00:11:29+0800 INFO]
ContinousBluetoothManager.keepConnectionAlive:
Sending keep alive message

[2026-08-13T00:11:29+0800 INFO]
ContinousBluetoothManager.keepConnectionAlive:
Sending keep alive message

[2026-08-13T00:11:29+0800 INFO]
ContinousBluetoothManager.keepConnectionAlive:
Sending keep alive message

[2026-08-13T00:11:29+0800 INFO]
ContinousBluetoothManager.keepConnectionAlive:
Sending keep alive message

[2026-08-13T00:11:29+0800 INFO]
ContinousBluetoothManager.keepConnectionAlive:
Sending keep alive message

Immediately afterwards:

[2026-08-13T00:11:29+0800 ERROR]
ContinousBluetoothManager.keepConnectionAlive:
Failed to keep connection alive:
A command is already running

[2026-08-13T00:11:29+0800 ERROR]
ContinousBluetoothManager.keepConnectionAlive:
Failed to keep connection alive:
A command is already running

[2026-08-13T00:11:29+0800 ERROR]
ContinousBluetoothManager.keepConnectionAlive:
Failed to keep connection alive:
A command is already running

[2026-08-13T00:11:29+0800 ERROR]
ContinousBluetoothManager.keepConnectionAlive:
Failed to keep connection alive:
A command is already running

Then the Bluetooth connection times out:

[2026-08-13T00:11:29+0800 ERROR]
BluetoothManager:
Failed to disconnect:
The connection has timed out unexpectedly.

One second later the pump reconnects:

[2026-08-13T00:11:30+0800 INFO]
BluetoothManager:
Connected to pump!

[2026-08-13T00:11:31+0800 INFO]
PeripheralManager:
Connection and encryption successful!

[2026-08-13T00:11:31+0800 INFO]
ContinousBluetoothManager.keepConnectionAlive:
Sending keep alive message

[2026-08-13T00:11:33+0800 ERROR]
ContinousBluetoothManager.keepConnectionAlive:
Failed to keep connection alive:
Timeout has been hit...

The same pattern repeats

At 00:13:37, six keep-alive calls were triggered at the same timestamp.

They were immediately followed by multiple:

A command is already running

errors, followed by:

Failed to disconnect: The connection has timed out unexpectedly.

and:

No connected device

The pump then reconnects again.

This pattern repeats continuously.


Multiple background jobs also appear in the log

At another point the log contains multiple messages like:

ContinousBluetoothManager.handleBackgroundTask:
Existed background job. Not connected anymore

ContinousBluetoothManager.handleBackgroundTask:
Existed background job. Not connected anymore

at the same timestamp.

This seems consistent with multiple background tasks existing simultaneously.


Additional command contention after reconnect

Another reconnect sequence shows several operations being started at almost exactly the same time:

Connected to pump
Connection and encryption successful
updateInitialState()
keepConnectionAlive()
syncPump()
updateInitialState()

Immediately afterwards the following operations fail:

updateInitialState:
A command is already running

syncUserOptions:
A command is already running

syncHistory:
A command is already running

fetchPumpTime:
A command is already running

This suggests that the keep-alive/background tasks may not only compete with each other, but also with normal Dana pump synchronization commands.

Potentially affected operations include:

  • syncPump
  • updateInitialState
  • syncUserOptions
  • syncHistory
  • fetchPumpTime
  • temp basal communication
  • bolus communication

Suspected cause

Looking at ContinousBluetoothManager.swift, handleBackgroundTask() appears to create an unstructured Task that periodically calls keepConnectionAlive() while the pump is connected.

However, the background Task does not appear to be stored in a property and explicitly cancelled before another background task is created after reconnect.

A possible sequence therefore seems to be:

Task A starts
↓
Task A sleeps between keep-alive calls
↓
Pump disconnects
↓
Pump reconnects
↓
Task B is created
↓
Task A wakes after the pump is connected again
↓
Task A sees isConnected == true
↓
Task A continues + Task B continues
↓
Two keep-alive tasks now exist

After another disconnect/reconnect:

Task A + Task B + Task C

and so on.

This would explain the observed progression:

2 → 3 → 4 → 5 → 6

simultaneous keep-alive calls after repeated reconnects.


Why this may produce "A command is already running"

PeripheralManager appears to allow only one outstanding pump command at a time.

If one command already owns the command/write queue, another command results in:

A command is already running

Therefore, if multiple keep-alive background tasks exist simultaneously, they may compete for the same command channel.

They may also compete with real pump operations such as synchronization or dosing-related communication.


Impact

During the affected period I observed:

  • Multiple simultaneous keep-alive calls
  • Repeated A command is already running
  • Repeated keep-alive failures
  • No connected device
  • Repeated disconnect timeouts
  • Automatic reconnect cycles
  • Pump synchronization failures
  • Communication failures around dosing operations
  • Failed/stuck Loop cycles
  • An extended period where normal closed-loop operation could not continue

CGM data remained available during the affected period.

Therefore, the Loop failure appeared to be related to Dana pump communication rather than missing CGM data.


Possible fix / question

Would it make sense for ContinousBluetoothManager to maintain exactly one keep-alive background Task?

For example, conceptually:

private var keepAliveTask: Task<Void, Never>?

Before creating a new task:

keepAliveTask?.cancel()

Then create/store the new task:

keepAliveTask = Task {
    while !Task.isCancelled && isConnected {
        keepConnectionAlive()

        try? await Task.sleep(
            nanoseconds: 60_000_000_000
        )
    }
}

The task could also be explicitly cancelled when the pump disconnects:

keepAliveTask?.cancel()
keepAliveTask = nil

before starting another keep-alive task after reconnect.

It may also be worth ensuring that keep-alive cannot compete with an active pump synchronization or dosing command.


Important

I have not tested the proposed code modification on a real pump.

The suggested fix above is only intended to illustrate the suspected lifecycle problem.

The suspected root cause is based on:

  1. The observed log pattern
  2. The increasing number of simultaneous keep-alive calls
  3. Repeated reconnect cycles
  4. A command is already running errors
  5. The current Continuous Bluetooth background-task structure

The particularly interesting evidence is that the number of simultaneous keep-alive calls increases progressively:

2 → 3 → 4 → 5 → 6

after successive reconnects.

This seems unlikely to be caused by normal Bluetooth signal loss alone.


Environment

  • Pump: Dana
  • Connection mode: Continuous Bluetooth
  • App: Loop / DanaKit
  • Encryption mode observed: RSv3
  • Date observed: 2026-08-13

Raw logs are not attached because they contain pump/Bluetooth identifiers.

I can provide additional sanitized log excerpts if needed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions