# Troubleshooting

Start with [Diagnostics](DIAGNOSTICS.md) and the log. See [Logging](LOGGING.md).

## Installation

**The installer reports a missing extension.** Enable `pdo_mysql`, `json` and `session` in the host's PHP selector. `mbstring` is advisory.

**The installer cannot connect to the database.** The host is usually not `localhost` on shared hosting. Confirm the user is granted on the database and that the name carries the account prefix cPanel adds.

**Every page 404s after upload.** `mod_rewrite` is off, or the `.htaccess` was not uploaded — dot-files are hidden in many FTP clients by default.

**Unstyled site.** Assets are not resolving; usually a base path problem. See [Subdirectory install](SUBDIRECTORY-INSTALL.md).

## Sign-in and email

**Password-reset email never arrives.** Configure SMTP and use the staged test; PHP `mail()` fails silently on many hosts. See [SMTP and email](../03-configuration/SMTP-EMAIL.md).

**The reset link points at the wrong domain.** `APP_URL` is unset or wrong. Links use the configured origin and never trust the request `Host` header.

**An editorial account cannot sign in.** That is enforced, not a bug. Editorial and system accounts have `login_disabled`. See [Users and roles](../02-administration/USERS-ROLES.md).

**Locked out entirely.** See [Recover admin access](../09-recipes/RECOVER-ADMIN-ACCESS.md).

## Admin panel

**`/admin` returns 404.** The administrator path is configurable. Check `site.admin_path`, or `config/site.local.php` if the operator kept it out of the database.

**A page shows a fatal error after an upgrade.** Check the log first. Run the database upgrade — a missing table from a pending migration surfaces this way.

**"Database upgrade required" never clears.** A migration failed part way. Read the log for the failing statement, fix the cause, and re-run; migrations are written to be safe to re-run.

## Content and display

**Home feed is empty but other pages work.** The feed query failed and the page degraded on purpose. The log has the reason.

**A widget vanished.** Widget rendering is isolated; a failing widget renders nothing and logs.

**Layout preset changes do nothing.** Presets emit `data-*` attributes on `<body>`. If the active theme's CSS does not select on them, nothing changes visually.

**Theme profiles tab is empty.** A fresh install adopts the live tokens as the first profile automatically. If it is still empty, the theme profile tables are missing — run the database upgrade.

## SEO

**Paginated pages canonicalise to page 1.** They should self-canonicalise. Verify the canonical policy is applied to that route.

**Sitemap is missing categories.** A category needs at least one published question **or** one published topic, and must not be `noindex`.

**Structured data shows the wrong answer count.** `answerCount` is the total across pages. If it matches the page size, the total is not being passed through.

## Performance

**A page is slow.** Measure before changing anything: query count, then query time. Add an index only with evidence. Do not add Redis or a queue — neither is a runtime dependency of this application.

## When reporting a problem

Include the version from Overview, the relevant log lines with secrets removed, `php bin/doctor.php` output, PHP and MySQL versions, and the hosting layout you used.

## A theme renders unstyled, and the console says the stylesheet was refused

```
Refused to apply style from '…/themes/orbit/assets/css/theme.css'
because its MIME type ('text/html') is not a supported stylesheet MIME type
```

The stylesheet is not being served. The request fell through to the front controller, which answered with the HTML 404 page — the browser then refused it because it is not CSS. The visible result is a site with no card borders or spacing, which reads as "the theme is broken" rather than as a routing fault.

This only happens in the **everything-in-`public_html`** layout, where the root `.htaccess` is what exposes static files at their public paths. Every directory under `public/` needs its own rule there:

```apache
RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^assets/(.*)$ public/assets/$1 [L]

RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^themes/(.*)$ public/themes/$1 [L]

RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^uploads/(.*)$ public/uploads/$1 [L]
```

If a request for `/assets/css/quaeriva.css` succeeds while `/themes/…/theme.css` fails, this is the cause: one directory has a rule and the other does not.

**Check first:** open the stylesheet URL directly. If it returns your 404 page instead of CSS, it is this. `tests/Public/RootRoutingRegression.php` fails the build when a public directory has no rule.

In the document-root-at-`public/` layout the root `.htaccess` is never read and this cannot happen.
