Skip to content
Pyrula
Pre-release: the packages are not on PyPI yet, so pip install does not resolve. Build from source to try it today.

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.

Terminal window
pyrula dev --check

Each 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.

A key that is not a project key:

Terminal window
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:

Terminal window
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: ConnectError

The interpreter is Python 3.11 to 3.15 and not the free-threaded build.

  • python.unsupported: Python is unsupported; use 3.11-3.15 (not the free-threaded build).

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.

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>.

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 reach to 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.

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 at
  • store.unreachable_reason: Cannot reach Valkey at :
  • store.denied: Valkey at refused 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).

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.

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

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

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.

That is not a dev check: it concerns an OpenTelemetry exporter. See Send existing traces.

View this page as Markdown