Local development and builds
Diagnose local issues in order: dependencies, initialization, development requests, content generation, and production builds.
First identify whether the problem occurs while installing dependencies, running a script, starting the server, or opening a page. A server listening on a port does not mean that configuration loading, database access, and page rendering have all succeeded.
1. Check the directory and runtime
Run all commands from the root of the application project created by the CLI, not from saavo-cli or the documentation website repository. Each repository has a different package.json, so their deployment commands are not interchangeable.
node --version
npm --versionThe current template checks require Node.js 22.13.0 or later, and the check script recommends Node.js 24. After switching versions, open a new terminal and confirm that the active version has changed.
If you see Missing script, check the package.json in your current directory first. The source archive used to initialize a project excludes the maintainer's commit script, and the application has no default deploy:prod script. See Project commands for the full list.
2. Keep the lockfile when installation fails
A project created from a released source archive may not initially contain package-lock.json. Use:
npm installIf you already have a valid lockfile that matches package.json and want to reinstall the locked versions, use npm ci. This command reinstalls dependencies and stops if the lockfile does not match.
When installation fails, identify the type of error first:
| Symptom | What to check |
|---|---|
| Network timeout or connection refused | Package registry, proxy, and network access |
| Node.js version mismatch | The version active in the current terminal, not just the installed versions |
| File in use or access denied | Whether the development server, editor, or another process is using the file |
| Lockfile does not match dependency declarations | Whether a dependency change was missed. Confirm this before updating the lockfile |
| Native dependency or platform package fails to load | Whether node_modules was copied from another machine. Install dependencies on the current platform |
Do not treat deleting the lockfile or forcing dependency upgrades as a general fix. These actions change many dependencies at once, making it difficult to tell whether the original problem was resolved.
3. Complete local initialization
After dependencies install successfully, run:
npm run saavo:initThe steps run in this order:
Create or retain .env and add SAAS_SECRET if missing
→ cf-typegen
→ db:migrate:local
→ doctorThis command neither overwrites an existing valid master secret nor creates remote Cloudflare resources. If the database step fails, .env and the type declarations may already have been generated. Fix the current error and continue. You do not need to delete and recreate the entire project.
Third-party service credentials in .env may still be empty after initialization. A Stripe or OAuth notice from doctor does not necessarily mean the entire application failed to initialize. Check the final error count first, then decide whether you currently need that service.
If SAAS_SECRET exists but has an invalid format, the script will not replace it automatically. For a new project, correct the configuration. For a project with existing data, recover the original secret before proceeding instead of overwriting it.
4. The development server will not start, or a page returns 500
npm run devAlways use the address and port shown in the terminal. The project generates the local site URL from the development server's port, so you do not need to keep changing VITE_SITE_URL in .env when the port changes.
| First error | Action | Expected result |
|---|---|---|
| Module not found | Complete dependency installation and confirm that the dependency directory was not copied from another platform | The original module error is gone |
| Configuration validation fails at startup | Check config/ and its references using the reported field | Configuration loads successfully during npm run doctor |
| Missing binding | Check wrangler.jsonc and run cf-typegen if needed | The corresponding resource is accessible at runtime |
no such table | Confirm the local database and run local migrations | The same page no longer reports a missing table |
| Page request returns 500 | Use the request time to find the exception stack trace in the terminal | The same request returns the expected page or application response |
cf-typegen only generates type declarations. An editor no longer reporting an error does not mean the binding exists at runtime. Do not temporarily replace Cloudflare resource access with in-memory objects to hide a configuration problem.
Local D1, KV, and other state are independent of remote resources. Deleting the .wrangler state directory may erase local data. Before troubleshooting, determine whether you need to keep that data.
5. Configuration fields are valid, but their combination is not
The project uses both TypeScript and Zod checks. Common issues include products referencing deleted roles, affiliate rules referencing nonexistent plans, invalid interval formats, and plain strings used where LocalizedText is required.
Find the configuration entry reported in the error, then inspect the objects it references. For example, after changing a product ID, check whether affiliate configuration, pages, and upgrade recommendations still use the old ID. Do not bypass validation with as any.
See Configuration files for field types and current defaults. The earlier tutorial may use its own consistent set of example names. When adapting it to your project, update references consistently rather than copying a single configuration fragment.
6. Documentation, blog content, or search does not update
Content updates involve three places: source files, generated collections and search indexes, and KV content read at runtime.
npm run gen:collectionsThis step checks whether the MDX compiles. If it fails, use the reported filename and line number to check frontmatter, unclosed JSX, code fences, and component properties. If navigation is missing, also check meta.json, locale directories, and the deploy.content switch.
To update the search index, run:
npm run gen:search-indexIf pages still load old content from local KV, also run:
npm run kv:sync:localThese operations serve different purposes. Regenerating collections does not prove that remote KV has been updated. For production content issues, see Deployment, resources, and domains.
7. Development works, but preview or build fails
npm run previewThe template's preview command runs build:preview first, then serves a local preview at 127.0.0.1:4173. Preview uses a bundled build. Do not assume its behavior is identical to npm run dev, especially for email delivery, environment variables, and checks that run only in production mode.
To isolate individual check stages, run:
npm run lint
npm run typecheck
npm run buildVite can generate build artifacts without a full type check, so a successful build does not replace typecheck. After fixing the code, run npm run verify to complete all project checks.
Use the exit code and the step that failed to determine the result. Some log messages are only warnings, but a final list of build files is no reason to ignore earlier type, content generation, or file write errors.
Verify the fix
Restart the project and repeat the page operation that failed. If you changed content, check navigation, page content, and search separately. If you changed initialization, confirm that existing local accounts and data still work. Clearing state can make a problem appear resolved without actually fixing it.