# Troubleshooting

The most common problems you'll hit building with the Fayz SDK — each one in a **problem → cause → fix** format. Start with the blank screen: it's by far the number one question from newcomers.

## `npm run dev` opens a placeholder screen / empty anchor

**Problem.** You generated the app, ran `npm install && npm run dev`, opened the browser and saw a bare-bones screen — a placeholder, an empty anchor, none of the admin shell the docs promise.

**Cause.** This is expected. The **runtime** that assembles the shell from your `app.manifest.json` ships with the Fayz platform, not in the public `@fayz-ai/sdk` package the scaffold installs today. The project you generated has the right manifest, plugins and config — but running locally against the public SDK, it doesn't yet have the app-runtime that renders any of it. The blank screen isn't a bug: it's the boundary between what's already public and what runs inside Fayz.

**Fix.** There's no "fix" — it's a property of the current stage. What to do instead:

- Treat the generated app as the **source of truth for config**: the manifest, the wired plugins and the file tree are already real and valid.
- To see the app rendered today, run it inside the Fayz platform.
- Understand why this boundary exists before spending time hunting for the "error".

{% callout type="warn" %}
This is the most common point of confusion for anyone trying the public SDK for the first time. Read [The two paths](/en/docs/two-paths) to understand who owns the shell, and [step 02 of the tutorial](/en/docs/tutorial/02-explore-the-anatomy) to see what each scaffold file does. The blank screen makes sense once you've read both.
{% /callout %}

## Blank screen / `useAuth must be used inside <AuthProvider>`

**Problem.** You build an app in code against the published packages, start the dev server, and the screen goes **blank**. In the console: `useAuth must be used inside <AuthProvider>` — even though `AuthProvider` clearly wraps the tree.

**Cause.** A version conflict producing **two copies of the React auth context**. Concretely, on the `0.6.x` line: `@fayz-ai/saas@0.6.0` depends on `plugin-auth ^0.1.0`; npm resolves that to `0.1.3`, which in turn requires `auth/core ^0.7.1`. The result is a **second** copy of `@fayz-ai/auth` in the `node_modules` tree. The `AuthProvider` from one copy and the `useAuth` from the other reference **different** React context objects — so as far as `useAuth` is concerned there's no provider above it, and it throws. The blank screen is that exception taking down the render.

**Fix.** Force a single version line across the whole auth axis with an `overrides` block in your app's `package.json`, pinned to the same line as the `saas` version you're on:

```json
{
  "overrides": {
    "@fayz-ai/plugin-auth": "0.1.3",
    "@fayz-ai/auth": "0.7.1",
    "@fayz-ai/core": "0.6.0",
    "@fayz-ai/ui": "0.6.0"
  }
}
```

Then delete `node_modules` and the lockfile and reinstall (`rm -rf node_modules package-lock.json && npm install`) so `overrides` actually collapses the two copies into one. Adjust the numbers to match your `@fayz-ai/saas` version — the point is that `plugin-auth`, `auth`, `core` and `ui` all land on a coherent line, with no second copy of the context.

## `fayz doctor` warns about plugins referenced in the manifest

**Problem.** You run `fayz doctor` and see a warning like:

```
⚠ manifest references plugin(s) [agenda, crm] — each id must resolve to an installed @fayz-ai/plugin-* factory wired in src/plugins.generated.ts or src/config/app.tsx
0 error(s), 1 warning(s)
```

**Cause.** This is a **warning, not an error** — note the `0 error(s)`. Every plugin `id` in the manifest has to resolve to a `@fayz-ai/plugin-*` factory that is actually **installed** (via npm) and **wired** into `src/plugins.generated.ts` or `src/config/app.tsx`. In the real model, a plugin is an installed npm package plus an entry in the plugins array — not something the runtime injects on its own. `doctor` lists the ids that aren't wired yet so you know what's left to install or connect.

**Fix.** Install the package for each referenced plugin (`npm i @fayz-ai/plugin-agenda`, …) and confirm it's wired into `src/plugins.generated.ts` or `src/config/app.tsx`. It's a warning, not an error — the `0 error(s)` count means the build won't break and CI won't fail; but each listed id only becomes a loaded plugin once it's installed and wired.

## `fayz doctor` rejects a custom theme in a code-first app

**Problem.** You have a **code-first** app — the real theme lives in the code-config (a `SaasTheme` with custom HSL values, perfectly valid) and `app.manifest.json` is vestigial. `fayz doctor` still rejects the manifest because `theme.brand` isn't one of the seven known brand names.

**Cause.** Today `doctor` validates the manifest **strictly**, with no notion that in a code-first app the manifest is just a leftover and the code-config is the source of truth for the theme. The manifest's `theme.brand` only accepts the seven built-in brand names; a valid custom HSL in your `SaasTheme` doesn't satisfy that check because the check never looks at the code-config.

**Fix.** Keep a **minimal, valid `theme.brand`** in the vestigial manifest — any of the seven brand names — purely to pass doctor, while the real theme stays in the code-config, which is what actually paints the app:

```json
{ "theme": { "brand": "violet" } }
```

The brand named here doesn't affect the rendered app (the code-config wins); it just keeps the manifest valid. It's an honest workaround, not a mistake on your side. Validation will become **path-aware** — recognizing code-first apps and letting the code-config be the theme's source of truth — in an upcoming SDK fix; until then, the named `brand` in the manifest is the bridge.

## `fayz db apply` complains about missing environment variables

**Problem.**

```
✗ Missing required Supabase credentials: SUPABASE_PROJECT_REF (or SUPABASE_REF), SUPABASE_PAT (or SUPABASE_ACCESS_TOKEN).
```

**Cause.** `fayz db apply` talks to the Supabase management API and **never** assumes default credentials. It needs two things: which project (`SUPABASE_PROJECT_REF`, or the alias `SUPABASE_REF`) and with which token (`SUPABASE_PAT`, or the alias `SUPABASE_ACCESS_TOKEN`).

**Fix.** Set both in your shell or in `<app>/.env.local` (which is git-ignored):

```bash
SUPABASE_PROJECT_REF=your-project-ref
SUPABASE_PAT=sbp_...
```

Where to find each one:

- **Access token (PAT):** Supabase dashboard → Account → Access Tokens → Generate new token.
- **Project ref:** Supabase dashboard → Project Settings → General (it's also the subdomain in your project URL).

## The dry run says "ships no migrations"

**Problem.** `fayz db apply --dry-run` prints a note:

```
installed @fayz-ai/db ships no migrations/ — the spine step is empty
(upgrade to @fayz-ai/db >= 0.1.3 once published)
```

**Cause.** The migration "spine" (the shared base schema) ships inside the `@fayz-ai/db` package. The version published today (0.1.2) doesn't bundle those `.sql` files yet — which is why the spine step comes out empty in the plan. The spine SQL lands in 0.1.3.

**Fix.** This is expected until 0.1.3 ships. `--dry-run` still shows the plan correctly (including your own migrations); only the `spine` step is empty. Once 0.1.3+ is published, `npm install @fayz-ai/db@latest` fills that step in automatically. See the [Changelog](/en/docs/resources/changelog).

## `db apply` refuses to run in a non-interactive shell

**Problem.**

```
✗ Refusing to apply migrations without confirmation in a non-interactive shell. Re-run with --yes to proceed.
```

**Cause.** Applying migrations is a destructive operation against a real database, so the command asks for a `y` confirmation. In a shell without a TTY (CI, a pipe, an AI agent) there's no way to type it — so the command **refuses** rather than hanging on stdin that will never arrive.

**Fix.** Pass `--yes` (or `-y`) to skip the prompt explicitly:

```bash
fayz db apply --yes
```

{% callout type="warn" %}
`--yes` applies migrations without asking for confirmation. Use it in deliberate automation, not as a habit in your local terminal.
{% /callout %}

See also: [Connect your agent](/en/docs/ai/connect-your-agent) — how an agent runs `fayz` commands with `AGENTS.md` in context.

## `fayz create` rejects the app name

**Problem.**

```
✗ Provide a kebab-case app name, e.g. fayz create admin my-app
```

**Cause.** The app name has to be **kebab-case**: lowercase letters, numbers and hyphens only, starting with a letter or number. Names with uppercase letters, spaces, `_` or special characters (`My Store`, `my_store`, `MyStore`) are rejected.

**Fix.** Use a valid slug:

```bash
fayz create admin my-store
```

## Port 5173 is already in use

**Problem.** When you run `npm run dev`, Vite either fails or starts on a different port because `5173` is taken.

**Cause.** The scaffold uses Vite, whose dev server binds to port `5173` by default. Another process (a previous instance that didn't die, another Vite app) is already holding the port.

**Fix.** Pick one:

- Free the port by killing the old process:
  ```bash
  lsof -ti:5173 | xargs kill
  ```
- Or run on a different port:
  ```bash
  npm run dev -- --port 5174
  ```
