Troubleshooting
Start with myxo check LAYOUT. It finds most of what follows, without starting anything. It reads the clients that services ask for from their source, without creating them. A client whose interface sits in a variable escapes it: a missing url or implementer then shows up in the log of a running process, as a service that keeps restarting.
The layout
| Message |
Cause and remedy |
unknown service NAME |
no installed package declares it. Check the entry point, then install the package again: uv sync |
NAME is placed more than once |
a service belongs to one process. Remove it from the others |
NAME is configured but not placed |
a [services.NAME] section with no process running it. Place it, or remove the section |
the layout needs the environment variable NAME |
${NAME} has no default. Set it, or write ${NAME:-default} |
entry point 'a' loads service 'b' |
the entry point's name differs from the class's name |
no process 'x' in layout (has ...) |
a mistyped process name for myxo serve |
myxo: error: choose a process: ... |
the layout has several processes, so myxo serve needs one named |
[services.NAME]: Object contains unknown field ... |
a mistyped key in that section. A wrong type reads Expected ..., with the field |
FILE: unknown key KEY in [processes.NAME] |
a mistyped key in that section, or at the top of the layout without in |
FILE: [processes.NAME]: Expected ... |
a value of the wrong type in that section, once the environment fills it |
FILE: Expected ... - at ... |
a value of the wrong type elsewhere in the layout, such as [supervisor] |
Services and interfaces
| Message |
Cause and remedy |
NAME is called over the network, so its process needs a url |
a process calls a service placed elsewhere. Give that service's process a url |
no service in the layout implements API (...) |
no service in the layout has that interface, or a remote one doesn't declare it in the myxo.apis entry points |
API: messages must be frozen Structs of immutable fields: ... |
a message allows mutation. Set frozen=True, and use tuples where you have lists |
API.op must take exactly one msgspec.Struct argument |
an operation has no message, or several. An operation with no input takes an empty struct |
x imports y: a must reach b through its interface, ... |
an import bypasses the interface, from another package or from another service's module in the same one. Import the interface module alone |
a service uses the framework's events, so the layout needs a `database` |
the layout has no top-level database. migrate names the framework's events, jobs, schedules or cache, and check and serve say the framework's services |
group G has two handlers for E |
two methods in one group handle one event type. Give one a group= |
process P is marked free-threaded, but ... |
the message names the reason: the Python build, PYTHON_GIL=1, or the module whose import turned the GIL back on |
S.m: no setting 'F' in its Config to take the period from |
@scheduled(every="F") names a field that the service's Config lacks. A field that holds no number gives is no number of seconds |
SQLite and replicas
| Message |
Cause and remedy |
process P cannot run N instances on SQLite, ... |
replicas need PostgreSQL. Set DATABASE_URL to a postgresql:// URL |
processes ... share the SQLite database ... from different hosts |
SQLite serves one host. Use Unix sockets on one host, or move to PostgreSQL |
one machine runs one instance of each process; ... |
myxo up runs one of each. Export for a platform to run more |
OperationalError, database is locked |
the writes outrun SQLite's single writer. See the rates that say when to move |
The database
| Symptom |
Cause and remedy |
the migrations cannot reach the database: ... |
a mistyped URL, or the server is down |
| a migration fails on a missing database or role |
Myxo creates tables. The database and its user must exist |
database unavailable, retrying in the log |
the framework's services wait for the database, and resume by themselves |
no such table or relation does not exist, on a myxo_ table |
the migrations have not run. Run myxo migrate after adding a framework service |
| events never arrive |
the publisher's database differs from the layout's database |
Running
| Symptom |
Cause and remedy |
myxo: GaveUp: NAME failed 6 times in 60s |
a service keeps failing. Read the tracebacks above it in the log |
the server could not start; its log says why |
most often a port already taken. Change PORT |
Unavailable: NAME is not running |
the service is restarting, or has not started. Handle it with retry, or a higher stage for callers in the same process |
Unavailable: ... no answer within 30.0 s |
a slow callee, or a blocked event loop. Put the work in a job, or raise call_timeout |
RemoteError |
the callee has a bug: its log has the traceback. With the answer is no T, the callee answered with another version of the message, as during an upgrade: run the same version on both sides |
| a new entry point is ignored |
the runtime reads the installed package. Install it again |
| each process takes seconds to start |
a user who can't write .pyc files, with no compiled bytecode. Install with --compile-bytecode |
/readyz answers 404 or nothing |
the process has no url, so it has no listener |
Exporting
| Message |
Cause and remedy |
for Kubernetes: processes.P.url must read ${MYXO_URL_P} |
see what the layout must read |
Kubernetes pods share no files, ...: is a variable unset? |
DATABASE_URL was unset at the export, so the layout fell back on SQLite |
Kubernetes needs the application's image (--image) |
give --image |
name the public process 'web', or use a layout with one process |
the export can't tell which process serves $PORT |
systemd runs one instance of each process; ... |
the same on Hop3. Only Kubernetes runs replicas |
Hop3 cannot reduce the privileges of P; ... |
accept that with --without-sandbox, or use another target |