Troubleshooting
A VM Patch passes through several stages between your push and a running fix on a device. When a patch doesn't take effect, the fastest way to find out why is to check the stages in order — each one has a clear signal.
The five stages
A patch only changes app behavior if it clears all five. Work through them top to bottom — the first one that fails is your answer.
- Resolved. The server returns a patch for the device's build.
- Downloaded & staged. The SDK fetches the patch and writes it to the engine's patch directory.
- Verified. On the next launch, the engine checks signature and build_id.
- Applied. The engine reconstructs (for diffs), checks the target hash, and loads the patched code.
- Stable. The patched app boots cleanly and doesn't trigger crash-revert.
Stage 0 — the app won't build
Build fails: asset assets/koolbase_build_id does not exist
Likely cause: koolbase_build_id is a build-time stamp the Koolbase CLI manages — koolbase build and koolbase release add the pubspec declaration (if missing) and write the file automatically. The error appears when the declaration already exists from a prior Koolbase build but the file itself isn't present (a fresh clone where it was gitignored, or a machine that hasn't run a Koolbase build), and you run a plain flutter run / flutter build, which doesn't invoke the CLI to regenerate it.
koolbase build once to regenerate it, or for an ordinary Flutter dev run create an empty one: touch assets/koolbase_build_id. An empty file is harmless — the real build_id is a hash of the compiled binary's instruction section, not this file's contents, and no patch resolution happens in a non-Koolbase build. Add assets/koolbase_build_id to .gitignore — it's a per-build artifact and shouldn't be committed.Stage 1 — the patch isn't being resolved
The device never downloads a patch
Likely cause: The device's build doesn't match a release that has a published patch.
Check the resolver directly
Stage 2 — it resolves but doesn't stage
The check happens but no patch is staged
Likely cause: The device couldn't complete the download — usually a network problem.
Stage 3 — it stages but fails verification
Rejected: signature
Likely cause: The patch wasn't signed by the key the app trusts.
Rejected: build_id mismatch
Likely cause: The patch targets a different binary than the one running.
Stage 4 — it verifies but the result is wrong
Rejected: reconstructed-target hash mismatch
Likely cause: A diff patch was reconstructed against the wrong base, or the base differs from what the diff was built against.
This rejection is the safety net working
Stage 5 — it applies but the app reverts
The patch applies once, then the app goes back to the old behavior
Likely cause: The patched code failed to boot cleanly, so crash-revert restored the last good version.
General tips
- Remember the next-launch rule. Patches stage on one launch and apply on the next. “Nothing changed” right after a download is expected — relaunch.
- Clearing app data resets patch state. A patch must be re-fetched after the app's data/cache is cleared.
- Test on the beta channel first. Prove a patch on your own devices before pushing to stable. See Rollout & Rollback.
- Validate locally with stage-local.
koolbase patch stage-locallets you confirm a patch applies on a device without going through the server.
iOS-specific checks
The patch never arrives on the device
First confirm the release matches: the build_id shown by patch push-ios (“derived from binary”) must be for the same binary your device is running — rebuilding the app changes its build_id, and a patch published for the old one will simply never be offered to the new one. If the release matches and the patch still doesn't arrive, note that patch checks are rate-limited per device; heavy relaunch loops during development can temporarily receive “no update”. Wait an hour, or use a fresh simulator/device identity.
The device downloaded the patch but still shows old behavior
iOS patches apply on the next cold launch — kill the app from the app switcher and relaunch from the icon. A flutter run hot restart is not a cold launch.
A previously applied patch stopped working after an app update
That's by design. An App Store update (or any rebuild) produces a new binary with a new build_id; patches pinned to the old binary are rejected and quarantined automatically, and the app runs the new binary's built-in code. Publish a new patch against the new build if the fix is still needed.
The function I patched didn't change
iOS patches target the functions your app declares patchable in koolbase_dynamic_interface.yaml. The declaration must be present in the shipped build — declaring a function after shipping doesn't make it patchable in the field. Declare your patchable surface before you build the release.