Troubleshooting
Symptom-first fixes for auth, CRUD, storage, AI, realtime, and deployment issues
Use this page when something looks broken and you need the shortest safe path from symptom to fix. Each row keeps the public contract small: no provider keys, session tokens, storage grant URLs, database URLs, or internal platform paths should appear in app code, logs, screenshots, or support posts.
Fast Checks
| Symptom | Likely cause | Fix | Verify |
|---|---|---|---|
Frontend calls http://localhost:5456 after deploy |
The auth or API client was created without the deployed base URL | Define one baseUrl from import.meta.env.VITE_API_URL and pass it to createAuthClient(baseUrl) and createGencowClient({ api, baseUrl, auth }) |
Browser network requests go to your *.gencow.app or custom domain backend |
Expected object from a query |
Required object input was omitted, or the app is using an older runtime before optional-object args were normalized | On current runtimes, useQuery(api.tasks.list) and useQuery(api.tasks.list, {}) both work for default CRUD lists. For required inputs, pass the full args object. |
CRUD list response is { data, total, nextCursor? }; required-input queries no longer run until args are present |
result.map is not a function |
createCrud().list returns an object, not an array |
Render result.data.map(...) and use result.total for counts |
Empty lists render an empty state instead of crashing |
| Private file URL returns 403 | Hosted storage is private by default | Return a fresh ctx.storage.createReadGrant(storageId, { ttlSeconds }) URL for browser previews, or store intentionally public assets with { visibility: "public" } |
Private bare URL is 403, read grant URL is 200, public asset URL is 200 |
| Login works locally but deployed frontend is logged out | Auth lifecycle and query/mutation calls use different origins | Use the same baseUrl for createAuthClient(baseUrl) and createGencowClient({ api, baseUrl, auth }) |
Sign in, refresh the page, then run an auth-required query |
| Realtime does not refresh a private list after a custom mutation | Custom mutations do not know which query to refresh | Call ctx.realtime.invalidate("tasks.list") after the write |
The subscribed useQuery(api.tasks.list, {}) refetches over HTTP with the user's JWT |
| AI request returns 402 or upgrade-required | The selected model or service usage requires a higher plan or available service credits | Pick an allowed model for the plan, reduce usage, or upgrade the plan | Error code is user-actionable and appears before provider call side effects |
| AI request returns 429 | Provider admission or service credit pacing is limiting the request | Retry later with backoff, lower request size, or check plan/provider status | Public logs show safe codes such as provider_tpm_limited, not raw prompts or provider keys |
| AI request returns 403 | Auth, plan entitlement, storage grant, or provider credential state may be blocking the request | First check app auth, plan/model access, and whether any private file was passed without a read grant | The response body should be public-safe and should not include secrets |
AI app redeploy fails with missing ai or @ai-sdk/openai |
Platform-provided dependency resolution drift | Redeploy with the latest CLI/runtime after the platform fix is released; do not add provider keys to app code as a workaround | Startup logs no longer show module resolution errors |
gencow app create reports a platform error |
The platform rejected the request before App creation completed | Keep the printed error Code and Correlation ID, confirm the App was not added with gencow app list, then retry only after platform health recovers or support confirms recovery |
A later create succeeds once, or support can trace the exact failed request without credentials or private logs |
gencow deploy says the login expired or is invalid |
The saved login or an environment deploy token is no longer accepted by the selected Platform | Follow the credential-source action printed by the CLI: log in again for saved credentials, or replace/unset the named GENCOW_TOKEN/GENCOW_DEPLOY_TOKEN variable |
gencow whoami and gencow app status <app> succeed against the intended Platform |
| Deploy reports a release-attempt conflict or recovery state | The same app has a release attempt or linked deployment that needs diagnosis | Run gencow deploy status --release-attempt <uuid> from the project; add --prod for the linked production app. Follow any linked deployment operation's retry gate. |
Status shows the attempt state, components, and deployment IDs; terminal attempt state alone does not authorize a replacement deploy |
gencow env set, unset, or push stores the value but says runtime projection is pending or failed |
The cloud env catalog changed, but the running backend has not acknowledged that exact env revision | Run the printed gencow env status --operation <id> command; do not repeat the mutation unless the status says retry is safe |
Status shows Runtime applied, or prints the failure class, correlation ID, retry precondition, and next action |
App returns 503 APP_INFRA_UNAVAILABLE |
The managed runtime is temporarily unable to start or reach platform-managed infrastructure | Retry after the suggested delay; if it persists, check gencow deploy status, review logs, and redeploy after platform status is healthy |
The response stays public-safe and does not include database URLs, tokens, or internal host details |
| Production static build points at localhost | VITE_API_URL was missing at frontend build time |
Build with VITE_API_URL=https://<app>.gencow.app bun run build, then deploy static files |
Generated JS contains the deployed origin, not localhost |
| Custom domain SSL or routing fails | DNS, apex/www policy, or active binding mismatch | Follow Hosting & Custom Domains, check gencow domain status, use www CNAME for the simple path, and keep apex/www policy explicit |
https://www.example.com serves the intended app or redirects only by configured policy |
Auth and API Base URL
Create one shared base URL and reuse it for auth lifecycle, queries, mutations, and realtime tickets.
// src/lib/gencow.ts
import { createAuthClient, createGencowClient } from "@gencow/client";
import { api } from "../gencow/api";
export const baseUrl = import.meta.env.VITE_API_URL ?? "http://localhost:5456";
export const auth = createAuthClient(baseUrl);
export const apiClient = createGencowClient({ api, baseUrl, auth });// src/main.tsx
import { GencowProvider } from "@gencow/react";
import { apiClient } from "./lib/gencow";
root.render(
<GencowProvider apiClient={apiClient}>
<App />
</GencowProvider>
);Do not rely on createAuthClient() to auto-read VITE_API_URL. Passing the URL explicitly is the stable contract.
CRUD Lists
createCrud().list returns an object with the rows and metadata. Treat it like a paginated result even when you only need the first page.
import { useQuery } from "@gencow/react";
import { api } from "../gencow/api";
export function TaskList() {
const { data: result, isLoading, error } = useQuery(api.tasks.list, {});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Could not load tasks.</p>;
if (!result || result.data.length === 0) return <p>No tasks yet.</p>;
return (
<ul>
{result.data.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}For paging, pass page and limit and disable navigation when there is nowhere to go.
const limit = 20;
const { data: result } = useQuery(api.tasks.list, { page, limit });
const totalPages = Math.max(1, Math.ceil((result?.total ?? 0) / limit));Storage 403s
Hosted storage is private by default. A private object should not be shown to the browser through a long-lived bare URL.
export const listFiles = procedure.query
.name("files.list")
.handler(async ({ context: ctx }) => {
const rows = await ctx.db.select().from(files);
return Promise.all(
rows.map(async (row) => ({
id: row.id,
name: row.name,
previewUrl: (await ctx.storage.createReadGrant(row.storageId, {
ttlSeconds: 300,
disposition: "inline",
})).url,
}))
);
});Use ctx.storage.getPublicUrl(storageId) only for assets that were stored with { visibility: "public" }.
AI Errors
| Code or status | Meaning | User action |
|---|---|---|
model_requires_upgrade / 402 |
The model is not available on the current plan | Choose a lower-tier model or upgrade |
provider_tpm_limited / provider_rpm_limited / 429 |
Provider admission is pacing the request | Retry later, reduce request size, or upgrade capacity |
| 403 | Auth, plan entitlement, storage grant, or provider credential state blocked the request | Check app auth, selected model, and whether private files use read grants |
server_error |
Unexpected platform or provider failure | Retry once, then check status/logs without exposing prompts or secrets |
For cloud apps, use generated gencow/ai.ts or createGencowAI() helpers. Do not put provider SDK keys or raw provider clients in app code to work around errors.
Runtime 503s
APP_INFRA_UNAVAILABLE means the hosted runtime could not complete a platform-managed startup dependency and the platform considers the state retryable. Retry after the response delay, then check gencow deploy status and logs. If the app keeps returning the same code after platform status is healthy, redeploy with the latest CLI/runtime. Do not paste response bodies, logs, or screenshots that contain tokens, database URLs, storage grant URLs, or provider keys into public support channels.
CLI App Creation Errors
When gencow app create fails, the CLI exits unsuccessfully after printing the platform error. Preserve the
Code and Correlation ID; they identify the exact request without exposing your login token. Run
gencow app list before retrying so you do not create a duplicate after an uncertain network result.
If the message says App creation is temporarily unavailable, wait for platform recovery or contact support with
only the safe error code, correlation ID, command time, CLI version, operating system, and runtime version. Do not
send credential files, tokens, full environment dumps, or private deployment logs. A native runtime message printed
after the platform error does not replace the first Code and Correlation ID as the support reference.
App creation itself does not start a runtime or allocate a tenant database. A runtime-capacity or deployment-executor issue should therefore appear during the first deployment, not as a partially created App. If an App exists but has not deployed successfully, it can still be deleted normally without requesting manual platform cleanup.
CLI Login Expired During Deploy
The CLI reports whether the rejected credential came from saved interactive login or an environment variable. For saved credentials, authenticate again against the same Platform:
npx gencow@latest login
npx gencow@latest whoamiIf the message names GENCOW_TOKEN or GENCOW_DEPLOY_TOKEN, replace or unset that variable (or its CI secret)
instead. Environment credentials override saved login credentials, so interactive login alone cannot replace an
expired environment token. Never paste the token into a support request. If the CLI prints a correlation ID, share
only that ID, the command time, Platform URL, CLI version, and operating system.
Deploy and Environment
Before deploying a frontend build, confirm the generated app talks to the intended backend:
VITE_API_URL=https://my-app.gencow.app bun run build
gencow deploy --static dist/If auth works in development but not after static deploy, rebuild the frontend with the deployed VITE_API_URL and redeploy the static assets.
For cloud env changes, Stored means the encrypted catalog update succeeded.
Runtime applied means a running backend acknowledged that exact revision. If
the CLI prints an env operation ID, inspect it with:
gencow env status --operation envop_123