Troubleshoot
How-to guide · for anyone who got an error from `pms`, the daemon, the client or a ring and wants the fix
Start with pms doctor; most environment problems surface there. Every error type in the workspace is listed variant by variant in the error catalogue; the entries below are the ones a user meets from the command line, with their remedies.
| You see | It means | Do this |
|---|---|---|
no mesh-hub daemon found (set PMS_DAEMON or run pms start) | No daemon on the default socket or address. | pms start, or point PMS_DAEMON at unix://<path> or ws://<host:port>. |
another daemon is live at /tmp/pms.sock | A daemon already owns that socket. | Use it, or start yours with a different --uds-path and --ws-bind. |
the socket at /tmp/pms.sock is served by user 502, not by this user | Another user’s process created the default socket, and the client will not send to it. | If it is stale, remove it and pms start; otherwise start your daemon with another --uds-path and set PMS_DAEMON=unix://<that path>. |
the daemon did not answer attach within 5s | The daemon accepted the connection but is not answering. | Check its log (MESH_HUB_LOG=debug); what holds it shows there. |
daemon error [name_in_use] | Another attached client holds that name. | Names are released on detach or disconnect; pick another, or find the client holding it with pms tap --json (the origin field). |
daemon error [limit_exceeded]: subscription limit reached: a connection may hold 64 (--max-subscriptions); unsubscribe one first | This connection already holds as many subscriptions as the daemon lets one connection hold. | Unsubscribe one, which frees a place, or raise --max-subscriptions on the daemon. |
daemon error [limit_exceeded]: connection limit reached: the daemon holds at most 256 connections (--max-connections); try again later on attach over the Unix socket | The daemon is at its connection cap and turned the connection away with that one error frame before closing the socket; the Rust client reports the daemon’s reason to the attach. The daemon logs connection refused: the daemon is at its connection limit at the first refusal and at each doubling. | Wait for a connection to close, or raise the flag; the daemon’s exit line counts connections_refused. |
transport error: ws connect …: HTTP error: 503 Service Unavailable | The daemon is at --max-connections; the WebSocket upgrade was refused with 503. | The same. |
daemon error [bad_request]: payload was not valid base64: … | The send payload is not padded standard base64: whitespace or a line break, the URL-safe alphabet, missing or misplaced padding, or a last symbol with stray bits; the message names the byte and its offset. Nothing was published and the connection is still open. | Encode with the standard alphabet and padding, as Python’s base64.b64encode, Node’s Buffer.toString('base64') and the browser’s btoa do. |
daemon error [payload_too_large]: frame of N bytes exceeds the M byte limit | The frame exceeded --max-message-bytes (1 MiB by default). The daemon answered and closed the connection, so the call that sent the frame, every call still waiting and Mesh::wait all report this reason. | Send less per envelope, or raise the flag on the daemon, then attach again. |
Mesh calls block and cannot run inside an async runtime | A Mesh method was called from a tokio task. | Call it from spawn_blocking, or from the callback thread, which is allowed. |
pms: the connection to mesh-hub ended: the daemon connection ended without a detach | The daemon went away, by a signal or a crash (its log ends with an exit line, or stops), and the connection ended without a detach; pms attach and pms tap print this and exit with status 2. A daemon that closed for a reason it stated, limit_exceeded or payload_too_large, prints that reason instead. | Restart the daemon, then the client. |
| WebSocket upgrade refused with 400 | The client did not announce pms-universal-port-v0. | Send Sec-WebSocket-Protocol: pms-universal-port-v0; the thin clients and the Rust client do. |
shared-memory object is N bytes but the requested capacity needs M | The object at that name is smaller than the ring or store the caller asked to map. | Open the ring by name alone: pms ring inspect and PulseRing::open_existing take the geometry from its header. |
shared-memory object is not a formatted ring: magic … | The object at that name is not a ring, or its creator never finished formatting it; a ring created before the header layout carries no magic. | Remove it, or start a daemon under that hub name, which replaces what it finds. |
ring header capacity mismatch: the region has N, the caller expects M (or version, slot_size) | The ring’s header records other geometry than the caller asked for. | Open it by name alone, or match the creator’s capacity and slot size. |
a ring of N slots cannot be made: it needs at least one slot, and its size in bytes must fit a file offset | A ring constructor was given a capacity of zero or one too large to size. Nothing was created. | Pass at least one slot. |
an envelope store of N slots cannot be made or opened: it needs at least one slot, at most M slots, and a size in bytes that fits a file offset | A store constructor or open was given a capacity of zero, above the header’s 32-bit field, or too large to size. Nothing was opened, created or mapped. | The same. |
could not reserve N bytes of shared memory: posix_fallocate failed with errno E | Linux could not reserve the pages of a new ring, store, region or NUMA backing: errno 28 (ENOSPC) means the tmpfs behind /dev/shm is full, errno 12 (ENOMEM) a memory limit, and errno 4 (EINTR) that signals interrupted all eight attempts the creator makes. Nothing was mapped and the half-made object was removed. | Give /dev/shm room: Run the daemon as a service gives the size each hub reserves and the container flag. A daemon whose store has no room logs it and runs without persistence. |
read-only hub: only the hub that created the ring publishes into it; send to the daemon over the port protocol instead | This process embedded a hub under a name whose ring another hub created, the daemon’s for instance, and a ring has one writer; publish, broadcast_envelope and route_envelope are refused before anything is written, while polling, subscribing and listening work. | Publish by sending to the daemon over the port protocol (Mesh::send), or make this process the one that creates the hub. |
envelope-store magic mismatch or envelope-store version mismatch | A store object from an older layout is still present under that name. | Start a daemon under that hub name: it unlinks what it finds and creates its own. On Linux just clean also removes /dev/shm/pms-*. |
| A tap prints nothing while pushes succeed | The filter does not match, or events were dropped: the daemon drops for a connection that falls behind, and the Rust client drops for a callback that cannot keep up. | Check the channel glob. The unsubscribe ack’s events_dropped counts what the daemon dropped for that subscription; Mesh::events_dropped() counts what the client dropped, and with_event_queue sets how many may wait. |
pms start says the daemon did not become ready | mesh-hub did not bind within the timeout. | Read the log path the message prints; a stale socket or a bound port is the usual cause. |
When the fix is not here
The daemon design and trust boundaries pages say what the daemon will and will not do; the specification’s port protocol chapter has every error code. Refutations and gaps are defects in this page; report them.