Skip to main content

Error handling

All SDK calls throw a typed EkycException subclass on platform errors. Catch the type you care about, and let unknown failures bubble.

try {
final r = await client.faceVerifyImages(imageA: a, imageB: b);
// success
} on EkycValidationException catch (e) {
// 400 / 422 — user-facing input problem
print('${e.type}: ${e.message}');
} on EkycAuthException catch (e) {
// 401 — token expired or invalid; ask the user to retry
} on EkycRateLimitException catch (e) {
await Future.delayed(e.retryAfter ?? const Duration(seconds: 1));
// retry
} on EkycFeatureDisabledException {
// the tenant doesn't have this feature on their plan
} on EkycException catch (e) {
// generic fallback — log e.requestId and surface a friendly message
}

Typed subclasses

SubclassWhen it firesHTTP statuses
EkycValidationExceptionInput rejected by the gateway or upstream service (bad image, missing field, etc.)400, 422
EkycAuthExceptionToken expired, missing, or invalid; tenant suspended / archived401, 403
EkycRateLimitExceptionRate cap exceeded. retryAfter is populated from the Retry-After header.429
EkycFeatureDisabledExceptionThe feature isn't enabled on the tenant's current plan, or the tenant has hit its hard cap.403
EkycException (base)Anything else the interceptor sees with the standard { "error": { "type", "message", "request_id" } } envelope.any

Fields

FieldTypeNotes
typeStringThe gateway's error type (validation_error, unauthorized, etc.). Maps to error.type in the wire envelope.
messageStringHuman-readable summary. Safe to surface to users for validation_error; otherwise log only.
requestIdStringAlways populated. Quote in support tickets.
statusCodeintHTTP status.
docUrlString?Link to a docs page describing the error type, if available.
retryAfterDuration?EkycRateLimitException only.

The v1 EkycError.code field is renamed to type (matches the wire envelope's actual field name). The v1 .param field is gone — when validation fails, the offending field is in message.

User-facing copy: userMessage

Added in v2.2.0. Every EkycException has a .userMessage extension getter that maps the structured type field to a short end-user-safe English sentence. Drop it straight into a snackbar or toast — the fallback for unknown types deliberately does not leak .message, so it's safe to render without a per-error allowlist.

try {
await client.faceVerifyImages(imageA: a, imageB: b);
} on EkycException catch (e) {
showSnackBar(e.userMessage);
log('${e.type}: ${e.message}', requestId: e.requestId);
}

.message and .requestId stay logged separately for diagnostics — .message can contain things like connect ECONNREFUSED 10.0.0.1 that are useful for support tickets but unsafe to show users.

English-only in v2.2.0; a localized-copy override hook is planned for a future revision.

v2.1.2 fix: callers can now on EkycException catch directly

Versions before v2.1.2 leaked Dio's internal envelope: the typed exception was attached as DioException.error and callers had to pattern-match on DioException catch (e) { if (e.error is EkycException) ... }. v2.1.2 fixed this — the _ekycCall<T> wrapper inside each public method strips the DioException envelope when the inner cause is an EkycException. The contract above (on EkycValidationException catch (e) etc.) works as documented.

Non-EkycException DioExceptions still propagate as DioException. Connection timeouts, DNS failures, TLS errors — these are network-level failures and the SDK doesn't fabricate a fake EkycException for them. Handle them via standard Dio idioms:

try {
final r = await client.faceVerifyImages(imageA: a, imageB: b);
} on EkycException catch (e) {
// platform-level error
} on DioException catch (e) {
// network-level error
if (e.type == DioExceptionType.connectionTimeout) { … }
}

tokenProvider failures

If your tokenProvider callback throws (e.g. your backend is unreachable), the SDK doesn't wrap it — the exception propagates as-is so you can match on its real type. The SDK does not retry your tokenProvider. That's your responsibility; add retry logic, circuit breakers, etc. inside the callback before returning to the SDK.

See Errors and retries for the platform-side error reference (every error.type value with its meaning and recommended client action).