Overview
Nosmai operations can fail because of application configuration, device state, permissions, network access, an invalid filter package, or an incorrect camera lifecycle.
A production integration should:
- check every asynchronous result
- keep technical details out of user-facing messages
- retry only temporary failures
- avoid retry loops for invalid configuration
- release the camera when a screen closes
- record enough context to reproduce a failure
- never record a complete license key
The exact error shape differs by platform:
| Platform | Main error form |
|---|---|
| Android | Exceptions and callback error strings |
| iOS | NSError with NosmaiErrorDomain and NosmaiErrorCode |
| Flutter | NosmaiError with NosmaiErrorType, code, message, and optional details |
Recommended error flow
Handle an operation in this order:
Start operation
|
v
Check immediate validation
|
v
Wait for asynchronous result
|
+---- success ----> update the interface
|
+---- failure ----> classify the error
|
+---- temporary ----> offer retry
|
+---- permission ----> explain required access
|
+---- configuration -> stop and fix configuration
Do not update a selected filter button before the apply completion reports success.
Initialization failures
Common initialization problems:
| Problem | Meaning | Action |
|---|---|---|
| Empty license key | The application supplied no usable key | Fix application configuration |
| Invalid key | The key does not exist, was revoked, or is incomplete | Check the full key in the Nosmai Console |
| Package mismatch | The key belongs to a different Android package name or iOS bundle identifier | Register and use the final application identifier |
| Platform mismatch | An Android key is used on iOS, or an iOS key is used on Android | Use the key issued for that platform |
| Expired license | The license is no longer active | Renew or replace the license |
| Unsupported SDK version | The installed SDK version is not accepted by the license service | Update to a supported release |
| First launch offline | No valid local license exists and the server cannot be reached | Connect the device and retry |
| Incorrect device time | The verification timestamp cannot be trusted | Enable automatic date and time |
| Monthly usage limit | The project reached its active-device allowance | Review the project plan |
Initialization should have one application-level owner. Starting it from several screens can produce confusing state and duplicate work.
License error codes
The native license service can report these stable codes:
| Code | Meaning | Retry |
|---|---|---|
LICENSE_EXPIRED | The license or subscription expired | No, renew first |
API_KEY_INVALID | The key is invalid, incomplete, or revoked | No, fix the key |
PACKAGE_ID_MISMATCH | The key does not match the installed application identifier | No, fix project configuration |
PLATFORM_MISMATCH | The key belongs to another platform | No, use the correct key |
SDK_VERSION_UNSUPPORTED | The installed SDK version is not supported | No, update the SDK |
MAU_LIMIT_EXCEEDED | The monthly active-device limit was reached | No, review the plan |
DEVICE_NOT_REGISTERED | The device is not accepted by the license configuration | No, review the project |
TIMESTAMP_INVALID | Device date or time is incorrect | After fixing device time |
TOO_MANY_REQUESTS | Too many verification requests were sent | Yes, with delay |
MISSING_FIELDS | Required verification information was not available | No, review integration |
NETWORK_ERROR | The license server could not be reached | Yes, when connectivity returns |
INTERNAL_ERROR | Verification could not complete because of a local or server error | Yes, once, then report |
Do not show these technical codes directly to an end user. Map them to a short explanation and a suitable action.
Network failures
License verification and cloud filter operations need network access. Camera frames and active local effects do not need to be uploaded for normal processing.
Common network conditions:
| Condition | Typical symptom | Action |
|---|---|---|
| DNS failure | Hostname cannot be resolved | Check internet, DNS, VPN, and private DNS settings |
| Connection failure | Server cannot be reached | Check firewall, proxy, VPN, and device network |
| Timeout | Request took too long | Retry with delay |
| TLS failure | Secure connection could not be verified | Check device time, certificates, proxy, and network inspection |
| Rate limit | Too many requests | Apply increasing retry delays |
| Interrupted transfer | Response ended before completion | Retry when the network is stable |
For example, cURL code 6 on Android means the device could not resolve the server hostname. It is a DNS or connectivity issue, not a camera or filter-rendering failure.
Retry policy
Use a bounded retry:
Attempt 1: immediately Attempt 2: after 1 second Attempt 3: after 3 seconds Attempt 4: after 8 seconds Then stop and wait for user action or a network-state change
Do not continuously retry invalid keys, package mismatches, expired licenses, or unsupported SDK versions.
Android error handling
Initialization
Android initialization validates required arguments immediately:
try {
NosmaiSDK.initialize(
getApplicationContext(),
BuildConfig.NOSMAI_LICENSE_KEY
);
} catch (IllegalArgumentException error) {
// Context or license key is missing.
} catch (RuntimeException error) {
// Native SDK initialization could not start.
}
NosmaiSDK.isInitialized() confirms that the Android API was initialized:
if (!NosmaiSDK.isInitialized()) {
// Do not open the camera screen.
}
This state does not replace asynchronous license-status handling inside the SDK. A temporary network failure can still be reported while the SDK evaluates a valid local license.
Filter apply
Always use the callback:
NosmaiEffects.applyEffect(
filterPath,
new NosmaiEffects.EffectCallback() {
@Override
public void onSuccess() {
// Update selected filter state.
}
@Override
public void onError(String errorMessage) {
// Keep the previous selected state and report the failure.
}
}
);
Android callback errors are descriptive strings. Do not parse them as a permanent numeric error contract. Use them for diagnostics and show a stable application message to the user.
Direct camera input errors
Listen for direct-input failures:
previewView.addOnOesInputErrorListener(reason -> {
// Stop the current Camera2 session.
// Reconnect using the normal Camera2 frame-input fallback.
});
A direct-input error should switch to the sample application's supported fallback. It should not leave the user on a black preview.
Recording
NosmaiSDK.startRecording(
previewView,
outputPath,
new NosmaiSDK.RecordingCallback() {
@Override
public void onStarted(
boolean success,
String error
) {
if (!success) {
// Re-enable the record button.
}
}
}
);
When stopping, wait for finalization:
NosmaiSDK.stopRecording(
new NosmaiSDK.RecordingCallback() {
@Override
public void onCompleted(
String outputPath,
boolean success,
String error
) {
if (!success) {
// Do not publish an incomplete file.
}
}
}
);
iOS error handling
Public error codes
iOS uses NosmaiErrorDomain with these NosmaiErrorCode values:
| Value | Constant | Meaning |
|---|---|---|
1000 | NosmaiErrorCodeUnknown | An unclassified failure |
1001 | NosmaiErrorCodeLicenseInvalid | The license was rejected |
1002 | NosmaiErrorCodeLicenseExpired | The license expired |
1003 | NosmaiErrorCodeNetworkError | A required network request failed |
1004 | NosmaiErrorCodeCameraPermissionDenied | Camera permission was denied |
1005 | NosmaiErrorCodeCameraNotAvailable | The camera is unavailable |
1006 | NosmaiErrorCodeEffectLoadFailed | A filter or effect could not load |
1007 | NosmaiErrorCodeInitializationFailed | SDK initialization failed |
1008 | NosmaiErrorCodeResourceNotFound | A required file was not found |
1009 | NosmaiErrorCodeInvalidParameter | A method received an invalid value |
1010 | NosmaiErrorCodeMemoryError | Required memory could not be allocated |
403 | NosmaiErrorCodeFeatureNotEnabled | The license does not include the requested feature |
Initialization
[[NosmaiCore shared]
initializeWithAPIKey:licenseKey
completion:^(BOOL success, NSError *error) {
if (success) {
return;
}
if ([error.domain isEqualToString:NosmaiErrorDomain]) {
switch ((NosmaiErrorCode)error.code) {
case NosmaiErrorCodeNetworkError:
// Offer retry when connectivity returns.
break;
case NosmaiErrorCodeLicenseInvalid:
case NosmaiErrorCodeLicenseExpired:
// Stop and review the license.
break;
default:
// Record error.localizedDescription.
break;
}
}
}];
Delegates
Set delegates after initialization:
NosmaiCore *core = [NosmaiCore shared]; core.delegate = self; core.camera.delegate = self; core.effects.delegate = self;
Receive general failures:
- (void)nosmaiDidFailWithError:(NSError *)error {
// Record the error domain, code, and safe context.
}
Receive camera failures:
- (void)nosmaiCameraDidFailWithError:(NSError *)error {
// Stop capture and show a retry or permission action.
}
Receive effect failures:
- (void)nosmaiEffectDidFailWithError:(NSError *)error
forEffect:(NSString *)effectID {
// Keep the previous selected effect state.
}
License status
The iOS delegate can report:
VALIDINVALIDEXPIREDUNVERIFIED
- (void)nosmaiDidChangeLicenseStatus:(BOOL)isValid
status:(NSString *)status {
if ([status isEqualToString:@"UNVERIFIED"]) {
// Wait for verification or a valid local-license decision.
}
}
UNVERIFIED is not the same as a definitive invalid license. It can occur while verification is pending or temporarily unavailable.
Flutter error handling
Flutter exposes NosmaiError:
class NosmaiError {
final NosmaiErrorType type;
final String code;
final String message;
final String? details;
}
Catch operation errors
try {
final applied =
await NosmaiFlutter.instance.applyEffect(filter.path);
if (!applied) {
// The package was not applied.
}
} on NosmaiError catch (error) {
final message = error.userMessage;
final canRetry = error.isRecoverable;
// Present message and offer retry only when appropriate.
}
Listen for native errors
StreamSubscription<NosmaiError>? errorSubscription;
void startErrorListener() {
errorSubscription =
NosmaiFlutter.instance.onError.listen((error) {
// Record error.type, error.code, and error.details.
});
}
Cancel the listener:
await errorSubscription?.cancel();
Flutter error types
| Type | Meaning |
|---|---|
unknown | An unclassified failure |
stateError | The requested action is not valid in the current state |
operationTimeout | The operation exceeded its allowed time |
platformError | The Android or iOS call failed |
networkError | A network request failed |
invalidParameter | A method received an invalid value |
sdkNotInitialized | A method was called before initialization |
invalidLicense | The license was rejected |
licenseExpired | The license expired |
cameraPermissionDenied | Camera access was denied |
cameraUnavailable | The camera cannot be opened |
cameraConfigurationFailed | The camera configuration was rejected |
cameraSwitchFailed | Front or back camera switch failed |
filterNotFound | The filter file does not exist |
filterInvalidFormat | The filter package is not valid |
filterLoadFailed | The filter package could not be loaded |
filterDownloadFailed | A cloud filter download failed |
recordingPermissionDenied | Required recording permission was denied |
recordingStorageFull | The device has insufficient free storage |
recordingWriteFailed | The output file could not be written |
recordingInProgress | Another recording is already active |
Preview errors
NosmaiCameraPreview(
onInitialized: () {
// Enable camera controls.
},
onError: (error) {
// Show a retry or permission message.
},
)
The preview error callback reports camera-view startup and resume failures. It does not replace onError, which can also report filter, license, network, and recording failures.
Camera permission problems
Permission denied
When permission is denied:
- stop the loading indicator
- explain why camera access is required
- show a retry action if the system can ask again
- show an application-settings action after permanent denial
- keep non-camera screens usable
Do not repeatedly trigger the system permission dialog without a user action.
Camera unavailable
The camera can be unavailable when:
- another application is using it
- another camera library in the same application owns it
- the previous camera screen did not release it
- the device is transitioning between foreground and background
- the camera service has temporarily failed
Stop every previous camera owner before retrying.
Black preview
Use this order:
- confirm initialization succeeded
- confirm camera permission is granted
- confirm the preview has a non-zero size
- confirm only one camera source is active
- confirm the old camera closed before a switch
- confirm the previous screen released its camera
- confirm the app returned to the foreground
- remove all active effects and test the plain preview
Android
Check:
NosmaiSDK.startProcessing(previewView)was called- Camera2 starts after the preview is ready
- the camera sends the correct size and orientation
- a direct-input error activates the normal Camera2 fallback
cameraHelper.stopCamera()runs when leaving the screen
iOS
Check:
- initialization completion reported success
attachToView:was called- the view has a valid frame
startCapturereturnedYES- no other
AVCaptureSessionowns the camera - the old view was detached before attaching a new one
Flutter
Check:
NosmaiFlutter.initializereturnedtrue- only one
NosmaiCameraPreviewis mounted - global cleanup is not running while a new preview is created
- the previous platform view finished disposal
- the preview widget has a valid size
Camera switching problems
Symptoms include:
- preview becomes black
- front camera works but back camera does not
- both cameras stop after switching back
- effect orientation becomes incorrect
Correct behavior:
- ignore repeated switch taps while a switch is active
- stop or reconfigure the old camera
- wait for the new camera source
- update front or back camera state
- update mirroring once
- resume effects after frames arrive
Do not open the new camera while the old Camera2 or AVFoundation session is still closing.
Orientation and mirroring problems
| Symptom | Likely cause |
|---|---|
| Preview is sideways | Camera sensor rotation was not converted to display orientation |
| Preview is upside down | Rotation direction is reversed |
| Front preview is not mirrored | Front preview mirror was not enabled |
| Face effect moves in the opposite horizontal direction | The frame and preview use different mirror rules |
| Effect is correct but recorded video is reversed | Preview and output mirroring were treated as the same setting |
Apply horizontal mirroring in one place. A double mirror can make the preview appear correct while face landmarks and effects use the wrong direction.
Filter does not load
Check:
- the file path exists
- the filename ends in
.nosmai - the package is complete and was not modified after creation
- the package supports the installed SDK version
- the license includes the required feature
- the apply completion or callback reports success
- the package type is one of the supported types
Supported package types:
filtereffectbeauty_effectbackground
Use applyEffect for all four types. Do not choose a lower-level apply method based on a guessed package type.
Local filter is missing from the list
Production discovery requires:
filter_name.nosmai filter_name_manifest.json filter_name_preview.png
The base name must match.
Check that:
- all three files are present
- the manifest JSON is valid
- the manifest type is supported
- the folder is included in the application bundle
- Flutter declares the folder in
pubspec.yaml - the local filter cache was refreshed after adding files
Use debug discovery for loose development packages. Do not use it as the production catalog.
Filter appears in the wrong category
The package manifest controls its category.
| Intended behavior | Manifest type |
|---|---|
| Full-frame color or LUT filter | filter |
| AR mask, sticker, particle, or 3D effect | effect |
| Packaged makeup or face beauty effect | beauty_effect |
| Packaged background effect | background |
Fix the package manifest. Do not relabel the returned item only in application UI.
Effect disappears or replaces another effect
Some external packages share the same active category.
Expected examples:
- applying a new regular
filterreplaces the previous regular filter - applying a new
effectreplaces the previous AR or beauty package - applying a
beauty_effectreplaces the previous AR or beauty package - a regular filter can remain active with an AR effect
Read active state after applying a package. Do not assume every package can remain active with every other package.
Face effect is delayed or unstable
Check:
- the preview remains close to the target frame rate
- the face is large enough and reasonably lit
- the camera frame orientation is correct
- frame timestamps increase normally
- the app is not doing expensive work on the main thread
- recording or streaming is not exceeding the device's capacity
- only visible features remain active
Do not solve tracking delay by skipping an uncontrolled number of face updates. A stale face result can make makeup detach during movement.
Background replacement problems
Nothing changes
Confirm that:
- advanced effects are included in the license
- the background method returned success where available
- image data is valid
- a video path is an accessible local file
- the device supports the required processing
Edge quality is poor
Test:
- even lighting
- visible separation between subject and background
- a supported camera resolution
- lower motion
- a simpler set of active effects
Hair and motion boundaries are more difficult than static clothing and wall boundaries.
Cloud filter problems
Catalog is empty
Check:
- cloud filters are enabled for the license
- the device has internet access
- the requested category is valid
- the requested compatibility version is supported
- pagination is not requesting a page beyond the final page
Cloud category values are:
effectsfilterbgbeauty_effect
Download fails
Check:
- filter ID is not empty
- network access remains available
- the device has free storage
- the application can write to its cache or files directory
- the item is still available in the catalog
Do not apply a cloud item until the download result provides a valid local path.
Recording problems
Recording does not start
Check:
- camera preview is already active
- no previous recording is starting, active, or stopping
- microphone permission is granted when audio is required
- the output location is writable
- the device has free storage
Recording stops but no file is available
Wait for the stop completion. Video finalization can continue briefly after the user taps stop.
Do not:
- close the camera screen before finalization
- upload the file before success
- start another recording while stop is pending
- delete or move the output path early
Application freezes
Common causes:
- blocking the main thread for every frame
- waiting synchronously for camera or graphics work
- applying many filters without waiting for the previous operation
- starting several downloads or decodes at once
- repeatedly creating and destroying the camera preview
- recording and streaming above the device's sustainable resolution
Actions:
- reproduce without active effects
- add one feature at a time
- measure preview frame rate
- inspect main-thread work
- test without recording or streaming
- test on another supported device
- capture a system trace if the freeze remains
Native crashes
Examples include:
UnsatisfiedLinkErroron AndroidSIGSEGVin a native library- graphics errors followed by process termination
- release-only crashes after code shrinking
Android UnsatisfiedLinkError
This normally means Java and native code do not match.
Check:
- the AAR comes from one SDK release
- no older SDK classes are copied into the app
- the final package includes
lib/arm64-v8a/libnosmai.so - the device is arm64-v8a
- code shrinking kept required JNI methods
- the application does not package two different Nosmai native libraries
Native graphics crash
Collect:
- complete crash stack
- device model and Android or iOS version
- SDK version
- camera position
- active filter names and types
- actions immediately before the crash
- whether recording or streaming was active
Do not hide a repeatable native crash behind an automatic restart. Report the reproducible sequence.
Performance troubleshooting
Use this isolation order:
- plain preview
- one regular color filter
- one face-tracked effect
- built-in beauty
- makeup
- background replacement
- recording
- live streaming
Record frame rate and device temperature at each step.
Common improvements:
- target a stable 30 FPS
- keep one camera preview active
- avoid invisible active effects
- use the recommended direct Android camera input with its device fallback
- reduce recording or streaming resolution on slower devices
- avoid large uncompressed filter textures
- prevent repeated filter changes while one apply is still running
- pause the camera when the app is not visible
Lifecycle troubleshooting
Camera indicator remains on
The camera source was not stopped.
- Android: stop Camera2 when the activity or fragment leaves the screen
- iOS: call
stopCaptureanddetachFromView - Flutter: remove
NosmaiCameraPreviewand allow its native view to dispose
Camera does not return
The old view may still own the camera, or global cleanup may overlap the new screen.
Use:
- pause and resume for temporary backgrounding
- stop and detach when leaving a camera screen
- full cleanup only when the application is finished with the SDK
Do not call full cleanup from every Flutter widget dispose method.
Logging
iOS
Enable SDK debug logging during investigation:
[[NosmaiCore shared] setDebugLoggingEnabled:YES];
Disable it for release:
[[NosmaiCore shared] setDebugLoggingEnabled:NO];
Android
Use Android Studio Logcat and filter by application package and Nosmai-related tags. The current public Android API does not expose the same logging toggle as iOS.
Flutter
Capture:
- Dart exceptions
NosmaiFlutter.instance.onError- Android Logcat when testing Android
- Xcode console when testing iOS
Flutter logs alone may not include the native cause of a camera or graphics failure.
Protect sensitive information
Before sharing logs:
- remove the complete Nosmai license key
- remove access tokens
- remove private live-stream channel tokens
- remove user identifiers
- review local file paths for personal information
Keep:
- SDK version
- application version
- platform and OS version
- device model
- error domain, code, and message
- filter type and non-sensitive filter identifier
- reproducible steps
Support report checklist
Include:
- platform
- device model
- OS version
- application version
- Nosmai SDK or Flutter package version
- debug or release build
- front or back camera
- active filter types
- whether recording or streaming was active
- exact reproduction steps
- complete relevant error or native crash stack
- result without active filters
Do not send only a screenshot of the final frame when reporting a crash or freeze. Include the logs and the action sequence that produced it.