Troubleshooting
pyrula dev runs these checks before it starts and prints only the ones that warn or
fail. pyrula dev --check prints all of them and exits 1 if any failed; add --json
for a machine-readable summary. Neither prints your key, a token, a URL credential or
anything a run handled.
pyrula dev --checkEach line is [status] id: detail, and a warning or failure adds a fix: line.
Angle brackets in the fixes below stand for a value from your setup.
Examples
Section titled “Examples”A key that is not a project key:
PYRULA_CLOUD_API_KEY=oops pyrula dev --check[fail] key: malformed fix: That is not a project key (pyr_<env>_...); a CI token (PYRULA_TOKEN) does not work here.An origin that does not answer:
PYRULA_CLOUD_API_KEY="<key>" PYRULA_CLOUD_URL=http://127.0.0.1:9 pyrula dev --check[fail] origin: http://127.0.0.1:9 unreachable fix: Cannot reach http://127.0.0.1:9: ConnectErrorChecks
Section titled “Checks”python
Section titled “python”The interpreter is Python 3.11 to 3.15 and not the free-threaded build.
python.unsupported: Pythonis unsupported; use 3.11-3.15 (not the free-threaded build).
packages
Section titled “packages”Which pyrula packages are installed, that the Cloud client is present when a key is set, that their versions agree, and that the web stack pyrula dev serves with is installed.
packages.client_missing: PYRULA_CLOUD_API_KEY is set but pyrula-cloud-client is not installed. Install it: pip install “pyrula[cloud]”packages.floor:needs , but <installed_name> is installed. Run: pip install -U <installed_name> packages.ui_missing: pyrula dev needs, a dependency of pyrula-workflows, and it is not installed (an install with —no-deps?). Run: pip install
PYRULA_CLOUD_API_KEY is present and shaped like a project key. Only the key marker and key id are ever printed.
key.absent: No PYRULA_CLOUD_API_KEY - running locally only. To see runs in Cloud, copy the key from Setup.key.malformed: That is not a project key (pyr__…); a CI token (PYRULA_TOKEN) does not work here.
origin
Section titled “origin”PYRULA_CLOUD_URL (or the default origin) is a valid URL, and with --check that it answers like a Pyrula API.
origin.url_without_key: PYRULA_CLOUD_URL is set but PYRULA_CLOUD_API_KEY is missing. Set the key, or unset the URL to run locally.origin.bad_url:origin.unreachable: Cannot reach: origin.not_api:is not a Pyrula API - use the API URL, not the dashboard URL. origin.bad_status:answered to the config read, so it may be down or behind a redirect; use the final https API URL. origin.mismatch:is not the URL this deployment publishes for itself. Set PYRULA_CLOUD_URL to <public_url>.
register
Section titled “register”Whether Cloud accepted the key. --check never registers a runtime, so it reports skipped; the answer appears when pyrula dev starts. A running runtime that Cloud starts refusing (401) holds its events and keeps retrying, so once Cloud accepts the key again it needs no restart: after a short refusal it re-registers within a minute (about 35 s in testing, a heartbeat tick plus a random offset of up to 30 s each) and delivers what it held within a minute after that (about 60 s from the fix). Each further refusal doubles the wait, up to 5 minutes, so after a long one allow about 7 minutes, or restart the process to register at once.
register.refused: Cloud refused this key: it may be revoked, expired, or from another deployment. Reveal or create one in Setup for the project and environment you want.register.unreachable: Cannot reachto register; retrying with backoff. register.rejected:answered to register, which is not a refusal of the key; retrying with backoff. register.no_token:answered register without a runtime token, so nothing can be sent. Check that PYRULA_CLOUD_URL points at the Cloud API, not the dashboard; retrying with backoff.
control
Section titled “control”Whether Cloud control is switched off.
control.off: Cloud is disabled by PYRULA_CLOUD_CONTROL=off.
Which store the run uses: that a SQLite file opens (once it exists), or that a Valkey answers and does not evict run state. An allkeys-* policy fails the check; a volatile-* one warns, since it can drop only finished-run history and a few short-lived keys, and on ElastiCache Serverless, which fixes it at volatile-lru, the warning names what to alarm on. A Valkey the store could not open (the connection refused or timed out, Lua scripting denied, the password rejected, a cluster behind a proxy) fails with the store’s own message under store.unopenable. The message carries the reason and, where it is known, the fix: the grant to add or the flag to pass. store.unreachable is for a failure that carries no message.
store.memory: Store: in-memory - Cloud shows this run, but recovery needs a durable store (drop —store memory:// to use .pyrula/dev.db, or pass —store URL)store.unreachable: Cannot reach Valkey atstore.unreachable_reason: Cannot reach Valkey at: store.denied: Valkey atrefused a command: . Grant the user that command (ACL SETUSER + ) or use a user that has it. store.unopenable: Cannot open the store: store.archive: Cannot use the Postgres archive from —postgres-dsn (): . Check the host, port, user, password and database in the DSN. store.eviction: Valkey maxmemory-policy is, so once memory fills it may evict keys pyrula keeps (a volatile-* policy only finished-run history and short-lived keys). store.wake_push: valkey: push wake-ups unavailable (); polling at each worker’s claim_poll_interval. Idle workers still claim every run, at that interval instead of the moment it is queued. The usual cause is a proxy that does not forward blocking commands or a user without +@blocking (ACL SETUSER +blpop).
outbox
Section titled “outbox”The on-disk spool for critical events can be created and written.
outbox.off: PYRULA_CLOUD_OUTBOX_PATH=off: critical events last only while this process runs.outbox.unwritable: The outbox directory is not writable; set PYRULA_CLOUD_OUTBOX_PATH to a path this user can write, or to off.outbox.quarantined: The outbox file was damaged, so a fresh one was started. Events it had not yet sent stay in the moved file and are not sent from there.outbox.open_failed: The outbox could not be opened, so critical events last only while this process runs. Check PYRULA_CLOUD_OUTBOX_PATH.
provider
Section titled “provider”For an agent target, whether a model is configured and a provider key and package are present. It never calls the provider.
provider.none_configured: No model is configured for this target. Set llm= on @agent, or pass —llm-ref.provider.bad_ref: —llm-ref does not resolve to a model instance. Pass module:name of an LLM object, e.g. your_module:llm.provider.no_key: No provider key found. Set ANTHROPIC_API_KEY or OPENAI_API_KEY, or the variable your own client reads.provider.package_missing: A provider key is set but its package is not installed: pip install
binding
Section titled “binding”A store bound to a different project than the key’s. Read by --check only, and only with a key and a durable store.
binding.mismatch: This store is bound to project <bound_project> (<bound_environment>), not the key’s project <key_project>. Run: pyrula store binding show
target
Section titled “target”The app file pyrula dev was pointed at: it must import cleanly and hold the named target, or exactly one when none is named. Read by --check only.
target.unloadable: Fix the error above, or name the target with pyrula dev app.py:, then rerun pyrula dev —check.
Traces accepted but no run
Section titled “Traces accepted but no run”That is not a dev check: it concerns an OpenTelemetry exporter. See
Send existing traces.