Skip to content

Error Handling

The Android SDK throws CircadifyError for expected SDK and API failures. Catch it around measureVitals() and branch on error.code.

class CircadifyError(
val code: CircadifyErrorCode,
override val message: String,
val details: Map<String, Any?>? = null,
cause: Throwable? = null,
) : Exception(message, cause) {
val isRetryable: Boolean
}
try {
val result = sdk.measureVitals(
MeasurementOptions(
lifecycleOwner = this@MainActivity,
previewView = previewView,
),
)
showResult(result)
} catch (error: CircadifyError) {
when (error.code) {
CircadifyErrorCode.CAMERA_PERMISSION_DENIED -> showPermissionPrompt()
CircadifyErrorCode.CAMERA_NOT_AVAILABLE -> showMessage("No camera found.")
CircadifyErrorCode.FACE_DETECTION_TIMEOUT -> showMessage("Make sure your face is visible and well lit.")
CircadifyErrorCode.CANCELLED -> Unit
else -> {
if (error.isRetryable) showRetry(error.message) else showMessage(error.message)
}
}
}
CodeDescriptionRetryable
MISSING_API_KEYNo API key was providedNo
INVALID_API_KEYAPI key is invalid or revokedNo
CodeDescriptionRetryable
CAMERA_PERMISSION_DENIEDCamera permission has not been grantedNo
CAMERA_NOT_AVAILABLENo usable camera was foundNo
CAMERA_IN_USECamera is already in use by another appNo
CodeDescriptionRetryable
CAPTURE_FAILEDCapture failed or timed out before enough frames were collectedNo
FACE_NOT_DETECTEDNo face was found in the camera feedNo
FACE_DETECTION_TIMEOUTNo scan-ready face was detected within the timeoutNo
QUALITY_TOO_LOWLighting, motion, or pose quality was too lowNo
CANCELLEDMeasurement was cancelledNo
CodeDescriptionRetryable
NETWORK_ERRORNetwork request failedYes
UPLOAD_FAILEDSecure upload failedYes
API_ERRORAPI returned an error responseNo
SESSION_EXPIREDSession expired before completionNo
SESSION_NOT_FOUNDSession does not existNo
PROCESSING_FAILEDProcessing failedNo
TIMEOUTPolling for results timed outYes
QUOTA_EXCEEDEDAccount quota was exceededNo
RATE_LIMITEDRequest rate limit was exceededYes
UNKNOWNUnexpected errorNo

Only retry when error.isRetryable is true.

suspend fun measureWithRetry(
sdk: CircadifySDK,
options: MeasurementOptions,
maxRetries: Int = 3,
): VitalSignsResult {
repeat(maxRetries) { attempt ->
try {
return sdk.measureVitals(options)
} catch (error: CircadifyError) {
if (!error.isRetryable || attempt == maxRetries - 1) throw error
kotlinx.coroutines.delay(1_000L * (1 shl attempt))
}
}
error("unreachable")
}