Troubleshooting¶
Symptoms grouped by layer, with the fastest fix and a pointer at the deeper module doc.
Install / setup¶
vendor/bin/flyok-setup install says db-schema: live schema matches declared — nothing to do even though I added a #[Table]¶
Discovery is reflection-based and cheap, but it depends on the module being registered via Registry::addModule(). Check that:
- Your package's
bootstrap.phpcallsRegistry::addModule(...). bootstrap.phpis incomposer.json'sautoload.files(or another autoloaded entry).composer dump-autoloadwas re-run after adding the package.- Your DTO
implements Flyokai\DataMate\Solid(the discovery filter requires it).
→ See flyokai/db-schema README.md, section How it works.
setup:install errors with LogicException: parameter $foo has union/intersection/no type¶
A #[Column] attribute is missing on a parameter whose PHP type the schema can't infer. Add #[Column(type: 'varchar', length: 64)] (or another explicit type) to that parameter.
My new #[Table] was registered but setup:upgrade reports attempting to drop column …¶
Drops are off by default. Either:
- Re-tag the column with
#[Column(name: 'new', renamedFrom: 'old')]so the differ emitsCHANGE COLUMNinstead of drop+add. - Set
FLYOK_DB_SCHEMA_ALLOW_DESTRUCTIVE=1and re-run.
The pending operations are listed in storage/setup/db-schema.pending-drops.php.
vendor/bin/flyok-setup install says parameter db-pass missing in CI¶
db-pass is a hidden-input option. In --no-interaction mode it must be passed on the command line:
It is not prompted, not auto-generated, and not read from environment by default.
bin/flyok-setup upgrade complains about absolute paths after I moved the install directory¶
The deployed scripts in bin/ have absolute paths baked in. Either:
- Re-run
vendor/bin/flyok-setup install(which writes fresh deployed scripts) — note this may also re-run init phase steps you don't want. - Manually edit
bin/flyok-bootstrap,bin/flyok-setup,bin/flyok-console,bin/flyok-clusterto fix theFLYOK_BP/FLYOK_VENDOR_PATHconstants.
DI / bootstrap¶
LifecycleException: service "X" must be started before get()¶
X is registered with singleton(definition, mustStart: true) and you're calling $container->get('X') before $application->start(). Either:
- Move the
get()call tobootstrap()instead ofinit()— the container exists during bootstrap, doesn't during init. - Drop the
mustStartflag if the service doesn't actually need ordered startup.
class X cannot be auto-resolved: ambiguous¶
Two definitions are registered with type X. automaticTypes() returns null on ambiguity. Disambiguate with:
names()->with('foo', singleton(...))— bind by parameter name on the consumer.types()->with(X::class, singleton(...))— explicit type-to-definition mapping.- An
AliasResolverImplentry — works only ifXis an interface.
Circular dependency detected (or infinite recursion)¶
The DI graph has a cycle. Common cause: two services have constructor parameters of each other's type.
- Refactor: introduce a third party (e.g. an event bus) that both depend on.
- Or: lazy initialise one side. Wrap with
injectableFactory(X::class)— the consumer gets a\Closurethat buildsXon demand instead of receiving an instance.
RuntimeException: routes cannot be added after Router::onStart()¶
You called httpRouterBuilder:default → addRoute(...) from a Bootstrap\Type\Worker::bootstrap() after the worker has already started. Move the route registration into Bootstrap\Type\Web::bootstrap() (which runs before start()).
Database¶
PendingOperationError: cannot start operation while one is in flight¶
Two fibers are sharing a single Connection instance and issuing concurrent queries. Either:
- Use the connection pool: get a fresh connection per fiber from
ConnectionPool::create(). - Serialise via a
Mutex.
MySqliException: 2006 server has gone away¶
AsyncMysqliConnectionPool retries on this code automatically (up to 10 times). If it's still failing:
- Increase
wait_timeoutin MySQL. - Check for any
set net_write_timeout/set net_read_timeoutoverrides. - If you're holding a long-running transaction, break it up.
Result iterator already advanced¶
Forward-only result; you tried to rewind() it. Use fetchAll() to buffer the rows into an array if you need to re-iterate.
Query is suddenly slower than expected¶
Ten possibilities; the fastest two checks:
- Did
db-schema → ApplySchemaadd an index that's now missing in production? Inspectstorage/setup/db-schema.last-run.php. - Did the
MysqlTypeNormalizerintroduce a change that triggered anALTER TABLE? Re-introspect via thedb-schema-statuscommand.
HTTP / OAuth¶
401 Unauthorized and the route is supposed to be public¶
Your handler implements ProtectedHandler instead of GuestHandler. The HandlerGuard runs Authorization::isAllowed() for protected handlers and rejects when there's no Bearer token.
403 Forbidden with a valid token¶
The handler's resourceId() is not in the granted scopes. Either:
- Adjust the OAuth client's scope.
- Adjust the user's role's resources in ACL (
acl:role:create, role JSON).
Server hangs under load¶
Use bin/flyok-cluster start (multi-process) instead of bin/flyok-console http:start (single-process). One fiber blocking on a slow query stalls everything in the same process; cluster mode parallelises across workers.
Async / event loop¶
RuntimeException: Cannot call EventLoop::run() from inside a fiber¶
You called EventLoop::run() from a callback or handler. Inside a fiber, use EventLoop::getSuspension()->suspend() instead. run() is {main} only.
Callback returns silently fail with InvalidCallbackError¶
Revolt requires callbacks to return void/null. Ensure your closure has no return statement (or returns null explicitly).
Stream callback never fires after fclose()¶
Closing a stream does not auto-cancel the registered callback. Capture the callback ID and EventLoop::cancel($id) before/after fclose().
Logging / introspection¶
Logs from cluster workers don't appear¶
Logs from worker processes are routed through IPC to the coordinator. If the coordinator isn't running (e.g. you started a worker directly), logs go to storage/var/log/cluster-worker.log. Check that file.
"Where is my dev TLS cert?"¶
storage/localhost.pem and storage/localhost.key.pem after flyok-setup install. They're self-signed; install in your OS keychain for local HTTPS.
When all else fails¶
# Re-sync agent assets (after a vendor update touched .agents/)
composer run sync-agent-assets
# Dump the live DI container (one-shot, slow on cold caches)
php bin/flyok-console doctor # if available — see "improvement #16"
# Force a clean reinstall (DESTRUCTIVE — wipes vendor/ and bin/)
rm -rf vendor bin storage
composer install
vendor/bin/flyok-setup install --no-interaction --db-host=… …