Docs / Troubleshooting
Most problems come down to a small number of causes, and those are listed here first. Work through the section that matches the symptom before assuming the engine did something unexpected.
A build that refuses to start does so early and on purpose. The checks below run before your code is decrypted, which is why the failure is clean and immediate rather than a confusing error somewhere deeper in the script.
| Cause | What you see | Fix |
|---|---|---|
| Version mismatch | The artifact refuses to start on the interpreter running it. | The version lock covers the interpreter minor version, operating system, architecture and pointer size, so a mismatch fails immediately with a clear error. Run the build under the minor version it targets, or rebuild for the version you actually run. |
| HWID mismatch | The build will not decrypt on the machine you moved it to. | The hardware fingerprint captured at build time is hashed into the decryption of the payload, so a different machine cannot read it. Run it on the authorised machine, or rebuild against the fingerprint of the machine you intend to use. |
| Trial expired | The artifact refuses to run once the deadline passes. | The deadline is bound to the build rather than to the host clock alone. Rebuild with a new trial window if the build was meant to stay live. |
| Clock rolled back | The artifact refuses to run on a machine whose clock moved backwards. | Rollback is detected, so winding a clock back does not buy time and breaks the build instead. Restore the clock, or rebuild. |
A version mismatch and an HWID mismatch are not the same problem. The first is about where the artifact runs, the second is about which machine it was sealed for. An artifact can be correct on both counts and still refuse on a third machine.
A protected build that starts and then dies mid-execution has almost certainly tripped the runtime defence layer rather than found a bug in your code. A single failure path handles every tamper trigger: it wipes key material and exits on the first strike.
That fail-closed response is deliberate. A build that carries on after it has detected tampering is a build that can be stepped through, so there is no option to continue past a trigger. Which detectors are armed depends on the security tier you selected: the Python-level tier covers the interpreter-side checks, the machine tier adds virtual-machine detection, and the top tier runs both together.
Any of the anti-debugger detection families firing terminates the process: debugger processes and modules, timing drift, hardware breakpoints, stack manipulation, trace functions and debug environment variables. Attaching to a protected build to inspect it is what the layer exists to stop.
Anti-VM detection scores CPU vendor strings, MAC address prefixes, BIOS and firmware data, hardware device enumeration and hypervisor flags. A build armed with virtual-machine detection can be killed for where it runs, not for what it does.
Driver detection kills on contact with known malicious kernel drivers. A script that legitimately loads kernel drivers can be caught by it. Leave that setting off for those scripts, and off for anything that drives hardware directly.
Integrity checks re-verify frozen module snapshots and function bytecode hashes at runtime, and reloading a protected module is blocked. A modified code object fails closed rather than running in a degraded state.
The virtual-machine caveat: if you test a protected build inside a VM, the anti-VM layer can fire even though nothing is wrong with the script. Test on the hardware you intend to ship to, or expect the kill. Driver detection is off by default and is the one setting here that most often needs to stay off, because it is the only detector that can be fooled by what your own script legitimately does.
These are failures at build time rather than run time, and each one is a missing dependency or a pair of settings that should not be on together.
Compiling the protected output to a native extension runs through Cython, which has to be present on the build machine. Install Cython there, or fall back to a handled output mode if you do not need a native extension.
Packaging to an executable needs PyInstaller on the build machine. Install it, or drop the executable output and ship a protected .py or a ZIP-packed build instead.
The two ZIP-packed layouts are alternatives, not layers that stack: one carries decoy entries, the other is a minimal single-entry container that is smaller and faster to build. Enable one. Zip-light is the default, so reaching for the decoy layout means switching layouts rather than adding one.
A failed job reports the reason in its error message rather than returning an artifact, so poll the job and read the message before changing settings. Re-submitting the same input unchanged will fail the same way.
If the artifact builds and runs but the protected script misbehaves, the cause is usually one transform that does not suit your source rather than the pipeline as a whole. Rebuild with the suspect setting off and compare the two runs.
The number converter rewrites numeric literals as hex, octal or binary. On a script that embeds images or other binary data as literals, that rewrite can corrupt the payload. Leave it off for those scripts.
That is expected. Decoy entries, encryption envelopes and per-section headers all add size on top of your source, and the artifact is not trying to be smaller than what went in.
A diagnostic build reports per-stage timings, transform counts, artifact size and format, and a key-material freshness check. It is the fastest way to see which stage touched the behaviour. Note that the resulting artifact is a diagnostic build, not a release build, so do not ship it.
A diagnostic build is for diagnosis only. It carries pipeline debug output and is not a release build, so switch the setting back off before you protect the version you ship.
Submissions are capped in several places at once, and the limits are independent. A request can be well under the per-minute rate and still be refused because the account already has its ceiling of jobs running.
| Limit | Value | What to do |
|---|---|---|
| File size | One .py file per request, maximum 10 MB. | The endpoint rejects the upload with File too large (max 10MB). Move embedded data out of the source, or protect the parts as separate jobs. |
| Per-IP rate | 60 requests per minute. | The endpoint returns 429 Too many requests. Slow the submission loop down and retry. |
| Per-account rate | 30 requests per minute. | Also 429 Too many requests. This ceiling applies to the account, wherever the requests come from, so parallel submitters share it. |
| Concurrency | 2 pending or processing jobs on pay as you go, 3 on standard dashboard plans, 5 on API plans. | The endpoint returns 429 Too many concurrent jobs (max N). Poll the running jobs to completion before submitting the next one. |
| Daily key quota | 1000 requests per day per external key. | The endpoint returns 429 Daily limit reached (1000/day). The quota is per key and per day, so a second key on the same account carries its own allowance. |
A 503 Too many concurrent submissions on this account. Retry in a moment. response means another submission raced this one at the same instant. The response carries a Retry-After header of one second, so pause briefly and send it again. Because the concurrency ceiling differs by plan, this arrives sooner on pay as you go than it does on an API plan.
Two status codes cover nearly every access problem, and they mean different things. One is a balance, the other is a plan.
| Status | Message | What it means |
|---|---|---|
| 402 | Insufficient credits. Please top up your account. | The plan's credit bucket is empty. Every obfuscation debits one credit, and nothing is unlimited on any plan, so this is a balance problem rather than an authentication one. |
| 403 | An active API subscription is required to use external API keys | The account holds no API bucket, or the one it had has ended. External keys need an API plan. The dashboard path is unaffected and still works on a dashboard plan. |
Credits are shared. The dashboard and the API debit the same balance, so protecting a file from the dashboard and protecting one through an external key both come out of the one bucket. There is no separate API allowance to fall back on once the bucket is empty.
The full API key is shown once, at creation, so a key you did not store cannot be recovered, only replaced. An account holds a small number of active keys, and each one has its own daily request quota.
If the failure is not one of the causes above, the Discord is the quickest route to an answer. It is monitored by the people who build the engine, and a report that includes the job id, the settings you submitted, the target Python version and the exact error you saw is the one that gets resolved fastest. The API reference lists every error the endpoint can return if you want to identify the code first.