Troubleshooting
Use this page when something breaks during install, first login, publishing, or frontend builds. Each entry starts with the thing you are likely to see, then gives the shortest useful fix.
Deeper runbooks:
| Area | Runbook |
|---|---|
| Package discovery | Debugging package discovery |
| Admin extensions | Debugging admin extensions |
| Public output | Debugging public output |
| Marketplace | Debugging Marketplace |
php artisan says permission denied
Section titled “php artisan says permission denied”When it happens: You cloned the project, copied files from another machine, or unpacked a deployment artifact.
Run:
chmod +x artisanchmod -R u+rwX storage bootstrap/cacheYou should see: php artisan about prints the Laravel environment summary.
composer require capell-app/capell cannot find the package
Section titled “composer require capell-app/capell cannot find the package”Why: Your app cannot see the private Capell repositories or Packagist credentials.
Check:
composer config repositoriescomposer show capell-app/capell --availableIf you install from VCS repositories, add the repository entries from the install guide. Then run:
composer clear-cachecomposer require capell-app/capell -WYou should see: Composer resolves capell-app/core, capell-app/admin, and capell-app/frontend.
/admin returns 403 or will not let you log in
Section titled “/admin returns 403 or will not let you log in”Why: The user exists, but Filament access or Capell roles were not assigned.
Run:
php artisan optimize:clearReplace [email protected] with the admin user email.
You should see: The user can open /admin and reach the dashboard.
The dashboard loads, but Pages or Settings are missing
Section titled “The dashboard loads, but Pages or Settings are missing”Why: Package resources, permissions, or Filament navigation were not registered.
Run:
php artisan optimize:clearphp artisan capell:admin-installYou should see: The sidebar includes Capell resources such as Pages, Media, and Settings.
Published pages still show old content
Section titled “Published pages still show old content”Why: You are looking at cached static HTML.
Run:
php artisan capell:html-cache:clearIf capell-app/html-cache is installed, also run php artisan capell:static-site to rebuild generated public HTML.
If your queue is not sync, keep a worker running:
php artisan queue:workYou should see: The frontend updates after refresh.
Published pages never generate
Section titled “Published pages never generate”Why: Capell dispatches static HTML generation through Laravel queues.
For local development, set:
QUEUE_CONNECTION=syncFor database or Redis queues, run:
php artisan queue:workYou should see: New or changed pages appear in public/page-cache.
The frontend shows Laravel’s default welcome page
Section titled “The frontend shows Laravel’s default welcome page”Why: The default / route is still taking priority.
Fix: Remove the default route from routes/web.php:
Route::get('/', function () { return view('welcome');});Then run:
php artisan optimize:clearYou should see: Capell handles the homepage route.
Can't resolve 'swiper/...' during npm run build
Section titled “Can't resolve 'swiper/...' during npm run build”Why: A symlinked package imports Swiper through an export path that your Tailwind/Vite setup cannot resolve.
Run:
php artisan capell:frontend-tailwind-assets --reportnpm installnpm run buildcapell:frontend-tailwind-assets is provided by capell-app/foundation-theme; check php artisan list capell if the command is not present.
If it still fails, add Vite aliases as described in Tailwind vendor CSS.
You should see: Vite finishes without unresolved Swiper imports.
Tailwind misses Capell admin styles
Section titled “Tailwind misses Capell admin styles”Why: Your Filament theme is not scanning Capell’s Blade views.
Fix: Add the Capell sources from theme compilation to resources/css/filament/admin/theme.css, then run:
npm run buildphp artisan optimize:clearYou should see: Capell admin fields, tables, and widgets render with the expected styling.
Laravel\Prompts\Exceptions\NonInteractiveValidationException: Required.
Section titled “Laravel\Prompts\Exceptions\NonInteractiveValidationException: Required.”Why: A Capell command asked for interactive input while running in CI, Docker, or --no-interaction.
Fix: Pass the missing values as flags. A full non-interactive install needs a site URL and either an existing user or new user details:
For a fresh app with no users yet, use:
php artisan capell:install \ --no-interaction \ --url=https://example.com \ --name="Admin User" \ --password="change-this-password"You should see: The command either completes or prints the next missing option explicitly.
Browser installer reports Preflight checks failed.
Section titled “Browser installer reports Preflight checks failed.”Why: The /install browser flow runs InstallerPreflight before install steps. Blocking failures usually mean the web PHP runtime cannot do something the CLI can: load a required extension, run subprocesses, write to storage or bootstrap/cache, connect to the database, store cache state, or resolve selected Composer packages.
Check the installer report first. It includes the failed check key and remediation text. Then compare the configured binaries:
php artisan config:show capell-installer.php_binaryphp artisan config:show capell-installer.composer_binaryphp artisan config:show cache.defaultphp artisan config:show queue.defaultIf optional packages were selected, run the Composer checks from the same app:
composer show capell-app/filamentors --availablecomposer config repositoriesFix: Set CAPELL_SETUP_PHP_BINARY to CLI PHP, set CAPELL_SETUP_COMPOSER_BINARY when Composer is not on the web user’s path, fix permissions, or repair Composer repository/auth access. For a fresh app using database cache before migrations exist, temporarily use file cache or create the cache table before retrying.
You should see: The preflight status changes from fail to pass or warning, and the browser installer can advance to install steps.
Browser installer progress says the install session expired
Section titled “Browser installer progress says the install session expired”Why: The browser installer stores progress under cache keys named capell.install.{installId}.* and protects concurrent installs with capell.install.lock. If cache is cleared, a volatile cache store restarts, or the install takes longer than the lock TTL, the progress route no longer has enough state to continue.
Check:
php artisan config:show cache.defaultphp artisan config:show session.driverphp artisan optimize:clearIf this happened after a long queued install, check that a worker is running:
php artisan queue:workFix: Restart the browser installer. For repeat failures, use a persistent cache store, avoid clearing cache mid-install, and keep the queue worker alive. On local/dev only, clear the stale installer lock if you know no install job is running.
You should see: A new install ID and fresh progress page.
/install says Capell is already installed
Section titled “/install says Capell is already installed”Why: EnsureNotInstalled treats Capell as installed when the Admin package is installed, the sites table exists, and at least one site row exists. This protects real sites from exposing setup forms.
Check:
php artisan route:list --name=capell-installerphp artisan tinkerIn Tinker:
Capell\Installer\Support\InstallerInstallationState::capellIsInstalled();Fix: Use the admin panel for a normal installed app. For a controlled local reinstall only, set:
CAPELL_SETUP_ALLOW_REINSTALL=trueThen clear cached config:
php artisan config:clearYou should see: /install opens the setup form again only in that controlled environment.
capell:install --developer-tooling fails while cloning a Capell GitHub repository
Section titled “capell:install --developer-tooling fails while cloning a Capell GitHub repository”Why: The installer reached Composer, but Composer cannot access one of the configured Capell repositories. This is separate from installer option validation. It usually means the app is configured with private vcs repositories but the current machine does not have valid GitHub credentials, or local path repositories were removed from composer.json.
Check:
composer config repositoriescomposer show capell-app/access-gatecomposer show capell-app/agent-bridge --availableFor local Capell development, prefer path repositories for locally checked-out packages. For production or CI installs from VCS, make sure Composer auth can access the private repositories before running:
php artisan capell:install \ --no-interaction \ --url=https://example.com \ --all-packages \ --developer-toolingYou should see: Composer installs capell-app/agent-bridge and then the installer continues to package setup.
Marketplace connect account fails or returns an invalid session
Section titled “Marketplace connect account fails or returns an invalid session”Why: Marketplace account linking creates a short-lived session, sends the admin to Capell App, then validates the returned state and code on the admin callback route. Failures usually come from a missing APP_URL host, a stale approval URL, expired session, wrong Marketplace API URL, or an admin session that is no longer authenticated.
Check config:
php artisan config:show app.urlphp artisan config:show capell-marketplace.marketplace.base_urlphp artisan route:list --name=capell-marketplaceCheck the latest account session:
select connection_session_id, claimed_domain, app_url, callback_url, status, expires_at, completed_at, last_errorfrom marketplace_account_connection_sessionsorder by id desclimit 5;Fix: Set APP_URL to a URL with a host, use https://capell.app/api/v1 unless you are intentionally testing another API, clear config cache, then start a new connection from the Marketplace page. Do not reuse old approval URLs after starting a newer connection.
You should see: A marketplace_instances row with an instance_id, account email, and last_heartbeat_at.
Marketplace domain verification cannot fetch the challenge
Section titled “Marketplace domain verification cannot fetch the challenge”Why: Capell App must fetch the exact public host and path recorded in marketplace_registration_sessions. A challenge can exist locally but still fail publicly because of host mismatch, auth middleware, redirects, CDN rules, maintenance mode, static-file routing, expired sessions, or page-cache rewrites.
Check:
select domain, challenge_id, challenge_path, status, expires_at, last_errorfrom marketplace_registration_sessionsorder by id desclimit 5;Then fetch the public URL from outside the server:
curl -i https://your-domain/.well-known/capell/marketplace/chal_EXAMPLEFix: Use the exact domain shown in the session row, make .well-known/capell/marketplace/* reachable without admin auth, and start a fresh verification if the session expired. Local hosts, IP addresses, .test, and .localhost can be account-linked but not publicly verified.
You should see: 200 OK, Content-Type: text/plain, and the challenge token body.
Marketplace catalogue loads but install is blocked
Section titled “Marketplace catalogue loads but install is blocked”Why: Browsing the catalogue only proves /extensions is reachable. Install authorization also needs the connected instance, licence/domain state, local compatibility checks, and any Marketplace policy required for that extension.
Check:
select instance_id, connection_mode, account_email, verified_domains, last_heartbeat_atfrom marketplace_instancesorder by last_heartbeat_at desclimit 5;Check the local package platform versions:
composer show capell-app/core filament/filament livewire/livewire laravel/frameworkFix: Connect a Capell account, resolve any diagnostics, activate or purchase the licence when requested, and follow the Marketplace action reason. If the browser looks stale during local debugging, clear local cache:
php artisan cache:clearYou should see: The Marketplace primary action changes to the next valid state, such as activate licence, install, installed, or incompatible with a clear reason.
Marketplace heartbeat or update check fails
Section titled “Marketplace heartbeat or update check fails”Why: PhoneHomeAction requires a Marketplace API URL, a public callback URL, a known instance ID, and network access to /instances/heartbeat.
Check:
php artisan config:show capell-marketplace.marketplace.base_urlphp artisan config:show app.urlphp artisan config:show capell-marketplace.marketplace.webhook_urlThen inspect local state:
select instance_id, last_heartbeat_atfrom marketplace_instancesorder by last_heartbeat_at desclimit 5;
select source, checked_at, capell_version, metadatafrom marketplace_update_advisory_snapshotsorder by checked_at desclimit 5;Fix: Connect the account if no instance exists. Set CAPELL_MARKETPLACE_WEBHOOK_URL when APP_URL is local, private, or not the URL Capell App should call. Confirm the server can reach the configured Marketplace API.
You should see: last_heartbeat_at updates and a new marketplace_update_advisory_snapshots row appears.
TypeError: ... __PHP_Incomplete_Class returned from SiteLoader::languages()
Section titled “TypeError: ... __PHP_Incomplete_Class returned from SiteLoader::languages()”Why: Older Capell builds could read a cached Eloquent collection before the Language model was loaded.
Fix:
composer update capell-app/core -Wphp artisan optimize:clearphp artisan cache:clearYou should see: The second request after cache warmup works normally.
UrlMissingSiteDomainException: Site domain not found for page ID ...
Section titled “UrlMissingSiteDomainException: Site domain not found for page ID ...”When it happens: /admin or a Livewire admin update fails while rendering a page table, dashboard widget, relation manager, or URL column. The stack usually ends in PageUrl::getFullUrlAttribute() after a table column calls $pageUrl->full_url.
Example trace:
Capell\Core\Models\PageUrl::getFullUrlAttribute()Illuminate\Database\Eloquent\Concerns\HasAttributesAwobaz\Compoships\ComposhipsCapell\Admin\Filament\Components\Tables\Columns\Page\PageNameColumnFilament\Tables\Columns\TextColumnLivewire\Mechanisms\HandleRequests\HandleRequests@handleUpdateWhy it occurs: PageUrl::siteDomain() is a composite relation. It matches page_urls.site_id and page_urls.language_id to site_domains.site_id and site_domains.language_id.
That relation cannot resolve if either of these is true:
- The data is incomplete: the
page_urlsrow points at a(site_id, language_id)pair with no matchingsite_domainsrow. - The model was under-selected: an eager load selected only columns like
idandurl, so Eloquent has aPageUrlinstance but not thesite_idandlanguage_idattributes needed by Compoships to build the relation query.
The second case is easy to spot in query logs. A broken lazy-load often looks like this:
select *from site_domainswhere site_domains.site_id is null and site_domains.site_id is not null and site_domains.language_id is not nulllimit 1;That contradictory site_id is null / site_id is not null condition means the relation was evaluated from a PageUrl model that did not have its composite key attributes loaded.
How this incident was traced: The failing stack pointed from PageUrl::getFullUrlAttribute() back to PageNameColumn::urlDescription(). That column rendered $page->pageUrl->full_url for the dashboard page list. The query log then showed Compoships trying to load siteDomain with site_id = null, which proved the accessor was not the source of the bad value; the admin rendering path was reaching full_url with a missing, incomplete, or non-renderable pageUrl relation.
How it was fixed: Admin display code now treats visit links as optional. PageNameColumn::urlDescription() resolves a renderable URL first:
- no persisted
pageUrl: do not render a link - under-selected
PageUrl: reload it withsiteDomain - no matching
siteDomain: do not render a link - valid
PageUrlandsiteDomain: render the short URL linked tofull_url
This keeps the admin panel available while preserving the stricter PageUrl::full_url behaviour for places where a real public URL must exist.
Check:
select id, pageable_id, site_id, language_id, urlfrom page_urlswhere pageable_id = 195 and pageable_type = 'page';
select id, site_id, language_id, scheme, domain, pathfrom site_domainswhere site_id = 4;Fix the data if needed: Add or repair the missing site_domains row for the page URL’s site and language. A page_urls row should not advertise a public URL for a site/language pair that cannot produce a host.
Fix the query if it was under-selected: Include every key used by downstream relations:
$query->select(['id', 'site_id', 'language_id', 'pageable_id', 'pageable_type', 'url']);Then eager-load siteDomain before rendering full_url:
$query->with('siteDomain');For page list surfaces, prefer loading enough relation data up front:
$query->with([ 'pageUrl.siteDomain', 'pageUrls' => fn ($query) => $query->with('siteDomain')->ordered(),]);Regression coverage: Keep a focused Livewire/Filament test that renders the affected admin surface with a page URL whose (site_id, language_id) has no matching site_domains row. The expected behaviour is that the table still renders the page and simply omits the broken visit link.
You should see: Admin tables and widgets render normally, and public/frontend URL generation still fails loudly if a real page URL has no matching domain.
php artisan test fails with plugins table not found or RouteNotFoundException
Section titled “php artisan test fails with plugins table not found or RouteNotFoundException”Why: Tests booted routes before Capell migrations had created the expected tables.
Run:
php artisan migratephp artisan testIf this happens in package tests, update to the latest Capell 4.x packages and clear Testbench state:
composer update capell-app/core capell-app/admin capell-app/frontend -Wcomposer clearcomposer preparecomposer testYou should see: Routes register, and tests fail only on real assertions rather than missing install tables.
Add a new troubleshooting entry
Section titled “Add a new troubleshooting entry”Use this shape:
- Put the exact error string in the heading.
- Explain the cause in plain language.
- Give the command or edit to make.
- Say what the user should see after the fix.