Troubleshooting and Error Codes
When a tk-ota-push (alias tkushpatch) command fails, the CLI returns a standard POSIX exit code (sysexits.h) and outputs a diagnostic error message to standard error (stderr).
This troubleshooting guide explains all CLI exit codes and provides step-by-step solutions to resolve build failures, authentication issues, and server rejections.
1. CLI Exit Code Reference
| Exit Code | POSIX Constant | Exact Cause in runner.dart | Quick Fix |
|---|---|---|---|
0 | EX_OK | Command executed successfully | No action required. |
1 | EX_SOFTWARE | General execution error, Flutter build failure, native code tampering detected, or HTTP 4xx/5xx API rejection | Inspect the detailed error message and follow diagnostic steps below. |
64 | EX_USAGE | Invalid command syntax, unrecognized option, or out-of-range argument value (e.g., --rollout 150) | Review usage syntax using tk-ota-push <command> --help. |
66 | EX_NOINPUT | No valid pubspec.yaml file found in the targeted directory | Verify your current working directory or pass --project <path>. |
69 | EX_UNAVAILABLE | Shorebird toolchain not found (~/.shorebird/bin/cache/flutter) | Install or initialize Shorebird on the system. |
2. Detailed Diagnostic & Step-by-Step Resolution
Error 1: Shorebird Toolchain Not Found (Exit Code 69)
Toolchain Shorebird introuvable (~/.shorebird/bin/cache/flutter).
Diagnostic
The Toolchain.locate() method inspects $HOME/.shorebird/bin/cache/flutter (or %USERPROFILE%\.shorebird\bin\cache\flutter on Windows). If this folder does not exist or lacks patched Flutter artifacts, execution aborts immediately.
Resolution
- Install Shorebird if not already present on the machine:
curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash - Initialize the patched Flutter engine cache by running doctor:
shorebird doctor - Verify that the cache folder exists and contains Flutter binaries:
ls -la ~/.shorebird/bin/cache/flutter
Error 2: No pubspec.yaml in . (Exit Code 66)
Aucun pubspec.yaml dans /Users/dev/workspace.
Diagnostic
The command was executed outside of a valid Flutter project root, or the path specified with --project points to a non-existent directory.
Resolution
- Navigate to your Flutter project directory before running commands:
cd path/to/my_flutter_app
tk-ota-push patch ... - Alternatively, provide an explicit project path:
tk-ota-push patch --project /path/to/my_flutter_app ...
Error 3: HTTP 409 Conflict — Release Already Locked (Exit Code 1)
❌ ERREUR HTTP 409 CONFLICT — PROTECTION CONTRE LE 'RELEASE OVERWRITE'
La Release '1.0.0+1' (android / aarch64) est DÉJÀ verrouillée dans database !
Empreinte SHA-256 de référence : 4f8b2c...
Diagnostic
TkushPatch enforces strict immutability for baseline Releases. Once a Release (e.g., 1.0.0+1) is locked on the server, its baseline binary can never be overwritten because deployed client devices rely on its exact byte footprint to compute differential binary diffs (binary diff).
Resolution
- If you intend to deliver an OTA bug fix without resubmitting to App Stores: use the
patchsubcommand instead ofrelease:tk-ota-push patch 1.0.0+1 -p android -m "Patch description" - If you are preparing a new App Store / Play Store binary: bump the version number in
pubspec.yaml(e.g.,1.0.0+2or1.0.1+1) before invokingrelease:tk-ota-push release 1.0.0+2 -p android --artifact aab
Error 4: Native Code Alteration Detected (Exit Code 1)
❌ ERREUR CRITIQUE D'INTÉGRITÉ NATIVE :
Des modifications de code natif ont été détectées entre la Release
de référence et le patch que vous tentez de publier :
- classes.dex : MODIFIÉ (Code Kotlin/Java altéré)
Diagnostic
The NativeIntegrityScanner detected changes in Android bytecode files (classes*.dex), the app manifest (AndroidManifest.xml), native C++ libraries, or iOS native frameworks. An OTA patch can only safely hot-swap Dart AOT code; pushing this patch would trigger fatal crashes on devices.
Resolution
- This update requires a full Store release. Bump the version in
pubspec.yamland publish a new Release:tk-ota-push release 1.0.1+2 -p android --artifact aab - If you are certain that the changes affect only pure Dart packages (and only
pubspec.lockdrifted), use the--forceflag:tk-ota-push patch 1.0.0+1 -p android --force -m "Pure Dart intl upgrade"
Error 5: PKCE Login Timeout (Exit Code 1)
Échec de la connexion : Délai dépassé (aucun retour de l'interface Web)
Diagnostic
tk-ota-push login spun up a local loopback HTTP server on 127.0.0.1, but no authorization callback arrived within the 3-minute timeout window (browser blocked, local firewall restricting loopback requests, or Web portal unreachable).
Resolution
- Verify the Web Console is reachable at the configured address (or specify
--web-url https://console-ota.enterprise.com). - Manually click the authorization link displayed in the terminal if your default browser did not open automatically.
- In headless CI/CD runners or remote SSH sessions, avoid
login: set theOTA_API_KEYenvironment variable instead.
Error 6: --rollout Must Be Between 0 and 100 (Exit Code 64)
Erreur : --rollout doit être un entier compris entre 0 et 100 (reçu: 150).
Diagnostic
The value provided to the --rollout option on patch is not a valid integer between 0 and 100.
Resolution
Provide an integer percentage between 0 and 100:
tk-ota-push patch 1.0.0+1 -p android --rollout 25 -m "Canary 25%"
Error 7: HTTP 403 Forbidden During Patch Promotion (Exit Code 1)
Erreur lors de la promotion (403) : {"error":"FORBIDDEN","message":"Patch must be in STAGED status and approved by a different user"}
Diagnostic
Promoting a patch to the stable channel enforces the 4-Eyes Governance Rule. The server rejects promotion if:
- The user attempting promotion authored the original patch (
author_id == approver_id). - The patch was not originally published with the
--stagedflag or has not received the required approval.
Resolution
- Log in with a separate account possessing approval permissions (
ROLE_SECURITY_OFFICERorROLE_TENANT_ADMIN). - Approve the patch via the Web Console (Governance tab) or execute
promoteusing the approver's API key.