# Troubleshooting

Common issues and their solutions. For cloud compiler specific issues, see [Cloud Compiler Troubleshooting](/compiler/cloud-compiler/troubleshooting).

## Compilation Issues

### "Feature must use defineTable()"

Your table file is missing the default export:

```typescript
// Wrong — no default export
export const applications = sqliteTable("applications", { ... });

// Correct — add defineTable default export
export const applications = sqliteTable("applications", { ... });
export default defineTable(applications, { ... });
```

### "File exports multiple tables with defineTable"

A single file can export multiple Drizzle tables, but only ONE can have a `defineTable()` default export. Split tables into separate files:

```
quickback/features/applications/
├── applications.ts           # defineTable(applications, { ... })
└── candidates.ts             # defineTable(candidates, { ... })
```

### Missing fields in generated API

If a field exists in your schema but doesn't appear in API responses:

1. **Guards:** Check that the field is in `createable` (for POST) or `updatable` (for PATCH). Fields not listed are silently stripped.
2. **Views:** If using views, the field must be in the view's `fields` array.
3. **Masking:** The field may be present but masked (showing `[REDACTED]` or `***`). Check your masking config.

### "organizationId" not auto-detected

The firewall auto-detects `organizationId` and `ownerId` columns by name. If your column uses a different name, declare the firewall explicitly:

```typescript
firewall: [
  { field: 'orgId', equals: 'ctx.activeOrgId' },
]
```

Or use `q.scope()` and override the SQL name:

```typescript
columns: {
  orgId: q.scope('organization', { sqlName: 'org_id' }),
}
```

## Migration Issues

### Columns persist after removal from schema

Quickback (via Drizzle) does **not** generate DROP COLUMN migrations. If you remove a column from your schema, the database column will remain as an orphaned column. SQLite handles this gracefully — the column won't appear in API responses since Drizzle only selects defined columns.

To clean up, you'd need to manually create a migration.

### Drizzle rename prompts in CI

If compilation fails with `drizzle-kit requested interactive rename input`, add explicit rename hints in your config. See [Drizzle rename prompts](/compiler/cloud-compiler/troubleshooting#drizzle-rename-prompts-in-ciheadless-compile).

### Cloudflare D1 destructive migrations

Quickback now adds a D1-specific safety pass around Drizzle-generated migrations for the `cloudflare-d1` provider:

1. **Better Auth cleanup** for retired managed tables is stripped instead of shipped as raw `DROP TABLE` SQL.
2. **Drop-only cleanup migrations** are reordered child-first when foreign keys require it.
3. **All D1 migration directories** generated by the compiler are replayed against SQLite during compile, so a D1-breaking migration fails before deploy.

If `quickback compile` now fails with a D1 replay error, treat that as a real migration problem instead of a deploy-time surprise. The generated SQL needs manual intervention or a schema change that Drizzle can express more safely.

## Runtime Issues

### 401 on all requests

1. **Missing auth:** Ensure your request includes `Authorization: Bearer <token>` or the session cookie
2. **Expired session:** Sessions last 7 days by default. Re-authenticate.
3. **Wrong auth URL:** Check that `BETTER_AUTH_URL` matches your deployment URL

### 403 Forbidden

1. **Wrong role:** Check the `access` config for the endpoint — your role may not be listed
2. **Wrong org:** You may be in a different organization than the record's `organizationId`
3. **Firewall hiding:** If `errorMode: 'hide'` is set, firewall violations return 404 instead of 403

### 400 Bad Request on create/update

1. **Guard violation:** You're sending a field not in `createable` or `updatable`. Check the error's `details.fields`.
2. **Missing required field:** A `.notNull()` column without a default is missing from your request body.
3. **Immutable field:** You're trying to update a field marked as `immutable`.

### Records missing from list

Records may be filtered by:
1. **Firewall:** Only records matching your organization (and optionally owner) are returned
2. **Soft delete:** Soft-deleted records are automatically excluded
3. **Access conditions:** Record-level conditions may filter results

### Recovering a previous `src/` tree

The CLI archives the previous `src/` tree to `quickback/.archives/src-<ISO-timestamp>/` before each compile (most recent 3 retained). If a generator regression silently dropped a file you depended on, restore it from the latest archive:

```bash
ls quickback/.archives/
# src-2026-04-27T16-31-02-014/   ← latest
# src-2026-04-27T16-29-44-882/
# src-2026-04-27T16-12-19-301/

# Restore the whole tree (back-out a bad compile):
rm -rf src
cp -R quickback/.archives/src-2026-04-27T16-31-02-014 src

# …or grep for what disappeared:
grep -r "missingExportName" quickback/.archives/src-2026-04-27T16-31-02-014/
```

Anything in `src/` not produced by the latest compile is removed on every compile — see [CLI → Compile Definitions](/compiler/cloud-compiler/cli#compile-definitions) for the full contract.

## See Also

- [Cloud Compiler Troubleshooting](/compiler/cloud-compiler/troubleshooting) — CLI and authentication issues
- [Errors](/compiler/targets/hono-api/using-the-api/errors) — Complete error code reference
- [Firewall](/compiler/definitions/firewall) — Data isolation configuration
- [Guards](/compiler/definitions/guards) — Field modification rules
