Troubleshooting

call invoke reports unavailable / "no eligible provider"

{"ok":false,"status":"unavailable","error":{"code":"unavailable","message":"no eligible provider"}}

exit code 7. This means exactly what it says: no connected provider has registered that service.method. It is the correct response whether the provider process hasn't started yet, already exited, or registered under a different service/method name than you invoked. It is not a configuration error — config validate on the same file will still succeed, because declaring a method in the Polycallfile only makes it invocable in principle (see Core concepts).

Checklist:

  1. Is the provider process actually still running? (Check its terminal — a crashed provider leaves no trace in the runtime's own output.)
  2. Does the provider's REGISTER message name match the Polycallfile exactly — same service, same method name?
  3. Did the provider connect to the same runtime.listen address the CLI's Polycallfile points at? Two different Polycallfiles with different ports will silently never see each other.

Authentication failures

The runtime and every provider (including provider.py and math_provider.c) must resolve the same credential:

  • security.mode = "token" — both sides need token_env pointing at the same environment variable, set to the same value, in their respective process environments.
  • security.mode = "local-auto" — both sides must run as the same OS user, since the credential lives in that user's Windows Credential Manager entry or ~/.polycall-credentials/<runtime name> file. A provider run as a different user (or, on a shared machine, a stale credential from a previous runtime.name) will fail AUTH — the provider prints provider: authentication failed: ... and exits nonzero rather than retrying silently.

If you changed [runtime].name in the Polycallfile, any previously stored local-auto credential is keyed to the old name and won't match; restart the runtime so it (re)generates the credential for the current name.

Port / listener conflicts

runtime start binding a port already in use fails at startup, before printing runtime ready, listening. If a previous run's process is still alive (see the shutdown section below), stop it first rather than changing the port purely to work around a stuck process — a changed port also requires updating any provider's Polycallfile to match, since the listen address is read from the same file.

Malformed request files

call invoke --input request.json expects a JSON object with a single args array of tagged values:

{"args":[{"type":"i64","value":"2"},{"type":"i64","value":"3"}]}

A missing type, a value that doesn't parse for its declared type, an argument count that doesn't match the method's declared params, or a type the method doesn't declare are all rejected — with math.add specifically, sending fewer or more than two arguments, a non-i64 type, or an out-of-range value all return a structured INVALID_ARGUMENT or provider_error from the provider's own validation (see Your first Python call), not a crash.

Loader / linking errors for a C consumer

If a consumer built against -Llib -lpolycall fails to run (not compile) with a missing-library error, that's the dynamic loader failing to find libpolycall.so/.dll at run time — a completely separate concern from the -L/-l link-time flags. See C library and ABI: the default build links the CLI statically precisely to avoid this class of problem; if you're linking dynamically, place the shared library beside your consumer or set an explicit loader path. Don't copy an unrelated downloaded DLL into a system directory to make the error go away.

Shutdown seems to hang

If polycall runtime stop or Ctrl+C doesn't return quickly on a build older than this session's fix, that's the Linux accept()/recv() shutdown bug described in Your first Python call — rebuild from current main, which fixes it. On any build, remember option placement rules apply to runtime stop too: polycall runtime stop --json ./Polycallfile works, polycall runtime stop ./Polycallfile --json is a usage error (exit 2) and never reaches the runtime at all — that's a parsing rejection, not a hang, but it looks similar if you're only watching for "did it print something."