Reference
Troubleshooting
Fix one thing at a time, then run the check again. Each row says what you see, what it usually means and what to do. If nothing fits, copy the exact error text and open an issue at github.com/philipposk/shipyard. Never include a key in it.
If you pasted a key into the chat by mistake, go to the provider's dashboard, revoke that key, make a new one and paste it only into the keys file. Chat history is saved, so treat a pasted key as leaked.
Setup
Start with these two checks. They tell you what is missing:
node "${CLAUDE_PLUGIN_ROOT}/scripts/setup-status.mjs"
node "${CLAUDE_PLUGIN_ROOT}/scripts/check-tools.mjs"
You do not usually run these yourself. Ask Claude to run them and read you the result.
| What you see |
What it means and what to do |
node or npx not found after installing Node |
The terminal started before the install. Close it, open a new one, run node --version |
node older than 20 |
Install the current LTS (long-term support) version from nodejs.org |
gh: command not found |
Install the GitHub tool, then reopen the terminal. Mac: brew install gh. Windows: winget install --id GitHub.cli |
gh auth status says not logged in |
Run gh auth login --web again. The one-time code expires after a few minutes |
git push asks for a password |
Run gh auth setup-git so git uses your GitHub login |
| The Vercel login page never finishes |
Close it, run npx --yes vercel@64.3.1 login again and pick "Continue with GitHub" |
vercel whoami says no credentials |
The login did not finish. Run the login again |
| The keys check says the Supabase token is missing |
Open the keys file again with secrets-init.mjs. The line must be SUPABASE_ACCESS_TOKEN=sbp_... with no spaces, quotes or line breaks. Save the file |
| The token looks right but the live check fails with 401 |
The token was revoked or copied only in part. Generate a new one and replace the whole line |
| The keys file opened in the wrong program |
Open ~/.shipyard/secrets.env with any text editor. On Windows it is %USERPROFILE%\.shipyard\secrets.env |
| The Playwright download fails |
Check your internet connection and run npx playwright install chromium again. Phone-size checks are skipped until it works, but apps still build |
| A Shipyard script cannot run on Windows |
Use the terminal Claude Code runs in. An old Command Prompt may not have Node |
| You hit your usage limit during setup |
Nothing is lost. Run /shipyard:setup later and it continues at the saved step |
| You are not sure what is saved |
Ask Claude to run profile.mjs show. It shows your saved answers and holds no secrets |
Building your first app
After the app is built, a check script looks at the live address and prints a tick or a cross for each part. You can run it again after every fix:
node "${CLAUDE_PLUGIN_ROOT}/scripts/verify.mjs" <appDir>
| Check that failed |
What it means and what to do |
| routes |
A page answered 404 or 500, or sign-up was blocked. A sign-up that answers 405 usually means the route is not public in src/proxy.ts |
| protection |
Visitors are stopped by a Vercel login wall (every visitor gets a 401). Run provision-vercel.mjs again, or turn off Deployment Protection in the Vercel project settings |
| auth |
A test user could not sign up or sign in. The cookie name in proxy.ts must match the one in src/lib/supabase/cookies.ts, and Supabase's redirect list must include your app address |
| isolation |
One user could see another user's rows. A table is missing row-level security or has a rule that is too wide. Add a new database migration and never loosen the test |
| browser |
A console error, a failed request or sideways scrolling at phone size. Fix what the check shows. If it was skipped, run npm install in the app and npx playwright install chromium |
| proxy |
src/proxy.ts is missing something. Add the missing prefix to PUBLIC_PREFIXES |
| secrets |
A key-shaped string is in a tracked file, or the secret server key is used somewhere it should not be. Remove it, rotate the key at the provider, and use the signed-in person's own database client in routes |
Other first-app problems:
| What you see |
What it means and what to do |
| Sign-in appears to work, then loops back to the sign-in page |
The login cookie name in proxy.ts does not match the one Supabase sets |
| Your app is slow or the database errors after a week away |
The free Supabase project paused after a week without use. Wake it in the Supabase dashboard with one click, then try again |
| Users say a sign-up link has expired on the first click |
Some mail programs open links before the person does and use them up. The page should be a button that confirms |
| The site shows an old version |
A stale service worker. It should cache only static files, never pages or data |
| A deploy rolled the site back to older code |
You deployed from unsaved local files and GitHub then deployed an older commit. Commit and push before you deploy |
| Vercel says ready but the address does not work |
A production deploy can end up staged and not live. Check the address and set the alias if needed. Trust the real address, not the dashboard |
Design
| What you see |
What it means and what to do |
| The slop check flags your pages |
It found generic patterns such as gradients, fake numbers or explanatory text under menus. Read the list and fix or accept each one |
| A chosen look is hard to read in one theme |
Light and dark are checked separately. Ask Claude to fix the contrast for the failing theme |
| A copied style looks like the other site's brand |
Take only colours, fonts and spacing, never logos, names, text or images |
Own address, email and legal pages
| What you see |
What it means and what to do |
| "Domain not found at registrar" |
Wait a little and check the DNS records for typos |
| Vercel says the domain belongs to another account |
Remove it from the other account first, then add it again |
| The domain check times out |
DNS can take longer than ten minutes. Try again in an hour |
| The domain check says "still opens as a second copy" |
The www address needs a redirect. In Vercel: your project, Settings, Domains, Edit next to www, Redirect to Another Domain, your main address, 308, Save. Then run the check again |
| The domain check says DNS is wrong but the site opens |
Some registrars flatten the main address, and Cloudflare's orange cloud shows its own addresses. At Cloudflare set the Vercel records to "DNS only" (grey cloud) |
| Resend stays "pending" |
A DNS record is missing, or your registrar added your domain name to the end of the record name. Paste the name without the domain part |
| Confirmation emails land in spam |
Wait a day, mark one email as not spam, and check the SPF and DKIM lines are verified in Resend |
| Password-reset emails only reach you |
Until your own sending domain is verified, they only reach the account owner |
| The script says it removed an older Resend key |
This happens on a new computer or when the sending domain changed. Deploy straight away, because the live app uses the old key until then |
| Legal generation stops and lists missing details |
It will not invent your name, address or email. Answer each question, then run it again |
Payments
| What you see |
What it means and what to do |
| The live-payments step refuses to run |
It lists what is missing: own domain, confirmed sign-ups, legal pages that name you, Vercel Pro, a working test mode or a passing launch check. Do each in order. Your country checklist is not checked by the script, so finish it yourself first |
| Checkout says the owner has to finish setting up |
Add your Terms link in the Stripe dashboard: Settings, Business, Public details. The link is your-address/terms |
| The test purchase worked but the app still shows the free plan |
The webhook did not arrive. Redeploy, then check the webhook in Stripe's dashboard |
| You change your domain after going live |
Payment webhooks do not follow redirects. Register the new endpoint first and use 308 redirects |
| You use a merchant of record |
Shipyard's billing module uses plain Stripe, so that part is not built for you. Stop at the pricing decision |
Test cards: 4242 4242 4242 4242 succeeds. 4000 0000 0000 0002 is declined. 4000 0025 0000 3155 asks for a code. Use any future date, any three digits and any name.
Phone and desktop apps
| What you see |
What it means and what to do |
| The phone shell refuses to start |
It needs a live HTTPS address. Finish the launch steps first |
| The app id is rejected |
It must look like com.example.myapp. Choose it carefully: it cannot change after you publish |
| The store check shows crosses |
It checks the app id, address, icons, privacy file, usage strings, account deletion, listing fields and web pages. Fix each cross |
| Apple rejects the app as "just a website" |
Add a native tab bar or a native feature to the main flow, a designed offline screen, no external payment links for digital goods, and a demo login in the listing notes. A review can still reject it |
| Google Play will not let you publish |
New personal accounts need a closed test with 12 testers for 14 days. Find testers early |
Running your app
| What you see |
What it means and what to do |
An issue labelled ops-alert appears |
The scheduled health check found a problem. Read the issue body first. It may be a paused Supabase project or an expired key, not a bug. Do not close it by hand: fix the cause and the next run closes it |
| Scheduled checks stopped |
GitHub pauses scheduled workflows after 60 days without activity in a public repository. Any push or manual run wakes them |
| Alert emails do not arrive |
They need Resend. Without it, errors are still logged on the admin logs page |
| Auto-merge never happens on a private repository |
GitHub does not enforce required checks on free private repositories. Make the repo public, upgrade, or merge by hand when the checks are green |
A pull request is labelled needs-review |
It touches something risky, such as sign-in, the database, payments or workflows. Read it and merge it yourself |
Product video
| What you see |
What it means and what to do |
| The render fails |
Usually a typo in a scene file, a scene id that does not match the script, or a missing timeline. Run npx tsc --noEmit in the video folder, and run the voice step again |
| A dead-air warning |
A stretch of half a second or more is frozen. Shorten the hold in the script, run the voice step again, or add some motion to that moment |
| The video engine is missing ffmpeg |
Install it with the line the setup script prints, then run the setup again |
| Disk space filling up |
Each render adds several megabytes. Delete old renders in the video out folder |
Working with the agent
| What you see |
What it means and what to do |
| A helper says "passed" but the thing is still broken |
A helper's report is not evidence. Ask for the flow to be run again and shown |
| Claude says it cannot do something |
Remind it that it can run commands and use the services' command-line tools, and ask it to try |
| Answers get slower and less accurate |
The conversation is too long. Start a new session and paste a short summary |
| Claude tried to read your keys file and was blocked |
That is the guard working. Scripts read keys directly and never print them |
Still stuck
Quote the exact error to Claude and ask what it means in plain English. If it is a bug in Shipyard, open an issue at github.com/philipposk/shipyard with the error text.
Back to Start, the practices or the costs.