Skip to content

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:

  1. Your package's bootstrap.php calls Registry::addModule(...).
  2. bootstrap.php is in composer.json's autoload.files (or another autoloaded entry).
  3. composer dump-autoload was re-run after adding the package.
  4. 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 emits CHANGE COLUMN instead of drop+add.
  • Set FLYOK_DB_SCHEMA_ALLOW_DESTRUCTIVE=1 and 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:

vendor/bin/flyok-setup install --no-interaction --db-pass= --db-host= 

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-cluster to fix the FLYOK_BP / FLYOK_VENDOR_PATH constants.

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 to bootstrap() instead of init() — the container exists during bootstrap, doesn't during init.
  • Drop the mustStart flag 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 AliasResolverImpl entry — works only if X is 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 \Closure that builds X on 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_timeout in MySQL.
  • Check for any set net_write_timeout / set net_read_timeout overrides.
  • 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 → ApplySchema add an index that's now missing in production? Inspect storage/setup/db-schema.last-run.php.
  • Did the MysqlTypeNormalizer introduce a change that triggered an ALTER TABLE? Re-introspect via the db-schema-status command.

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=