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 --version

The 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 install

If 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:

SymptomWhat to check
Network timeout or connection refusedPackage registry, proxy, and network access
Node.js version mismatchThe version active in the current terminal, not just the installed versions
File in use or access deniedWhether the development server, editor, or another process is using the file
Lockfile does not match dependency declarationsWhether a dependency change was missed. Confirm this before updating the lockfile
Native dependency or platform package fails to loadWhether 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:init

The steps run in this order:

Create or retain .env and add SAAS_SECRET if missing
→ cf-typegen
→ db:migrate:local
→ doctor

This 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 dev

Always 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 errorActionExpected result
Module not foundComplete dependency installation and confirm that the dependency directory was not copied from another platformThe original module error is gone
Configuration validation fails at startupCheck config/ and its references using the reported fieldConfiguration loads successfully during npm run doctor
Missing bindingCheck wrangler.jsonc and run cf-typegen if neededThe corresponding resource is accessible at runtime
no such tableConfirm the local database and run local migrationsThe same page no longer reports a missing table
Page request returns 500Use the request time to find the exception stack trace in the terminalThe 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:collections

This 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-index

If pages still load old content from local KV, also run:

npm run kv:sync:local

These 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 preview

The 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 build

Vite 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.