Error Codes and Tracking State (v3.3.2)

In most cases no code changes are required to upgrade from 3.3.1 to 3.3.2. The error cases your app receives are unchanged, and new features are only needed when you want them. Please check only 5. Where the error case changes and 6. Where the behavior changes below.

1. Tracking keeps going in more situations

Situation3.3.13.3.2
A Bluetooth device such as AirPods is connected or disconnected during tracking and the audio engine fails to restartTracking ends with didFail(error: .audioInitializationFailed)didInterrupt() → automatic resume → didResume(). If another app is using audio, the SDK checks again every 30 seconds, and if it still cannot resume, interruptionRecoveryFailed
After Siri reads a notification aloud while music is playingRecording sometimes did not restartThe SDK tries to resume once Siri finishes reading
An alarm turns off while a resume retry is scheduleddidResume() could be called twiceThe retry is skipped if tracking has already resumed

If the audio engine fails while not tracking, it is audioInitializationFailed as before.

2. 5-digit error codes (Beta) cleaned up

The numbers of the 5-digit error code (error.errorCode), provided as Beta since 3.2.0, are now organized by cause, and you can branch on them by name. Cleaning up the cases and the official switch to 5-digit codes are planned for v4.0.0.

Branching by code name

func didFail(error: Asleep.AsleepError) {
    switch error.errorCode {
    case .createConflict:           // 22409 the previous session is not closed yet
        // ...
    case .commonExpired:            // 11403 plan expired · too many requests
        // ...
    case .uploadTrackingTerminated: // 23499 tracking ended due to an upload error
        // ...
    default:
        print(error.errorCode.code, error.errorCode.message ?? "")
    }
}
  • Code that branches on cases (case .httpStatus, case .startTrackingNetworkFail, etc.) keeps working as before.
  • startTrackingNetworkFail and stopTrackingNetworkFail now show a deprecation warning. Only the warning is new; the behavior is the same, and they are delivered as before until v4.0.0.
  • For the full code table, see Error Codes.

errorCode.message

If the server sent a message, it is included here. It is for checking the cause (logging) and may change without notice, so branch your app logic on the number (code) or the code name.

== compares only the number

Because message holds the server message and can differ each time, == on AsleepErrorCode compares only code.

// 3.3.2: true regardless of the server message
[.audioSilenced, .audioUnsilenced, .uploadFailed].contains(error.errorCode)

3. Changed codes

In the situations below, the error number your app receives (errorCode.code) differs from 3.3.1. If you branch on this number or collect statistics with it, please check.

Situation3.3.13.3.2
Session creation 4002200022400
Session creation 403 · 429 (plan expired · too many requests)2200011403
Session creation 404 (user does not exist)2200011404
startTracking() while already tracking2200011010
Recorder failed to start2200011003
Upload 5xx1150023500
init server 5xx1150021500
init other failures · no internet1100021000
Token issue 401 · 404 (invalid appId / appSecret)1200012002
Report 400 · 401 · 404 · 5xx · other11xxx32400 · 32401 · 32404 · 32500 · 32000
Report list 400 · 401 · 5xx · other11xxx33400 · 33401 · 33500 · 33000
Average report 400 · 404 · 5xx · other11xxx34400 · 34404 · 34500 · 34000
Session deletion 401 · 5xx · other11xxx25401 · 25500 · 25000
User deletion 401 · 404 · 5xx · other · no internet11xxx26401 · 11404 · 26500 · 26000 · 26000
Analysis request 400 · 401 · 404 · 5xx · other11xxx31400 · 31401 · 31404 · 31500 · 31000
Metadata save rejected by the server with 4002700027400
ODA model file corrupted1000210001

4. Overlapping calls (tracking state)

When another call overlaps while tracking is starting, in progress, or stopping, it is handled as below.

CallWhen3.3.13.3.2
createSleepTrackingManagerStarting · tracking · stoppingReplaced with a new manager (the previous manager's recording could stop without notice)Returns the current manager as is. The newly passed delegate · config · recording options are not used
startTracking()StartingThe second call also proceedsIgnored (no callback)
startTracking()Stopping · init in progressStarts a new trackingIgnored (no callback)
startTracking()TrackingalreadyTrackingalreadyTracking (same)
stopTracking()StartingOnly the recorder stopsStops normally as soon as starting finishes
initAsleepConfigBefore the previous init respondsBoth proceed, callback twiceIgnored (no callback)
initAsleepConfigTrackingRecording stops without noticeCloses the session normally and sends didClose, then proceeds with init (didFail on failure)
setupStarting · tracking · stoppingIgnored only while trackingIgnored in all three

Asleep.isSleepTrackingAlive()

A function that tells whether tracking is in progress has been added.

if Asleep.isSleepTrackingAlive() {
    // starting · tracking · stopping
}
  • Returns true while starting after startTracking(), while tracking, and while stopping after stopTracking().
  • The state is kept only in memory. After the app is terminated and launched again, it is false.

5. Where the error case changes

In 3.3.x the error cases your app receives are not supposed to change, but the following do.

Situation3.3.13.3.2
Engine restart fails after a Bluetooth device is connected · disconnected during trackingaudioInitializationFailed (tracking ends)didInterrupt() → didResume(), or interruptionRecoveryFailed if it cannot resume
No response to the request that closes the previous session during initstopTrackingNetworkFailhttpStatus (errorCode 21000)
Token issue 401 · 404tokenIssueFailedtokenInvalidCredentials
Tracking starts before ODA setup has finishedunableODAsetupIncomplete (new, 10000)
Metadata save rejected by the server with 400saveMetadataFailedmetadataValidationFailed (with the server message)

6. Where the behavior changes

  • The analysis completion notification (didComplete) is decided by the function you called. Only a manager created with createSleepTrackingManager(config:delegate:recordingPath:recordingType:) checks for analysis completion and sends didComplete. If you pass an AsleepCompletableTrackingDelegate to createSleepTrackingManager(config:delegate:), 3.3.1 sent didComplete but 3.3.2 does not. didCreate(sessionId:) is called in both cases as before.
  • createSleepTrackingManager during tracking returns the current manager instead of a new one. Apps that created a new manager per screen to change the delegate will receive callbacks on the delegate passed first while tracking.
  • When the last chunk arrives after the session is closed right after tracking ends and is rejected, 3.3.1 sent uploadTrackingTerminated, but 3.3.2 does not. A rejection for the session being tracked is handled as before.

7. Good to know

📘

Recording can pause until the app is opened when an emergency or missing-person alert arrives

If an emergency or missing-person alert arrives while the app is in the background, the alert sound briefly takes the audio, recording pauses, and didFail(error: .cannotActivateInBackground) may be called. Because iOS does not allow turning audio back on in the background, the SDK cannot resume right away. When the app returns to the foreground, the SDK resumes recording automatically. Guide the user to open the app, for example with a local notification.


Did this page help you?