Where to look first
In development, the frontend forwards every
console call, uncaught error and unhandled promise rejection to its dev server, so a crash that never calls console still lands in .forge/logs/dev/frontend_<name>.log with its stack.
forge generate stops
“edited after forge wrote it”. You (or an agent) changed a generated file. Forge refuses to overwrite the edit. Move the change into the owned file beside it (for example handlers_crud.go rather than handlers_crud_ops_gen.go, or <entity>_repo_ext.go rather than the ORM file), then delete the edit. If the generated form genuinely cannot express it, forge project disown <path> --reason "<why>" takes the file over permanently.
The validation build fails. forge generate compiles the tree before it finishes and rolls back if the build fails, naming the file. If the failing line is in your own code, a change to the proto or a library usually broke it: fix it and re-run. If it is in a file forge reconciles (such as internal/app/compose.go), fix what that line was derived from: the component’s Deps or constructor.
This forge is not the version the project pins. forge.yaml’s forge_version names the forge the project was generated with, and generated code expects the matching library. Use that forge version, or upgrade the project: forge project upgrade list shows the migration notes between versions. --allow-version-skew overrides the check deliberately.
A required dependency has no provider. A component’s Deps field names a type nothing in the project constructs. Add a provider function in internal/app/providers.go, or mark the field // forge:optional-dep if it may legitimately be nil.
forge lint fails
Run forge lint --quiet to see only the failures, with the verdict on the last line. Each finding names its rule, and forge project capabilities explains every analyzer. Common ones:
- A list filter that is not
optional. Generated filters silently do nothing without presence, so a filter field on aList…Requestmust beoptional. - More than one service in a proto file. Split it: one service per file.
- A migration hazard.
forge lint --migration-safetynames the statement (aNOT NULLcolumn on a populated table, a volatile default) and the comment that opts out when you mean it. - The frontend cannot be a static export.
forge lint --static-exportnames the dynamic route, server action orcookies()call, with the fix. - A tool is missing. Lint fails rather than skipping when
golangci-lintorbufis unavailable;forge tools installinstalls the code generators.
forge env up does not come up
- A port is busy. Each worktree of a project gets its own block of ports, so parallel checkouts do not collide.
forge env pslists every stack running on the machine, across projects; stop the one you meant withforge env down <env>from its directory. - “air is not installed”. The API runs under air for hot reload. Install it (
go install github.com/air-verse/air@latest) or run once withforge env up dev -D host_runner=go-run. - A service did not bind in time. Startup includes compiling. On a slow machine, raise the wait with
--host-ready-timeout 5m. A timeout is not a crash: the summary lists the pending ports and log files. - The database is empty. Seeding runs only on a fresh database where every table is empty.
forge db seed statusshows what is there;forge db resetdrops, recreates, migrates and reseeds the dev database. - Docker is not running. Postgres, the dev identity provider and the observability stack run in Docker Compose.
A deploy does not go live
forge env deploy exits with a code that says what happened:
Other common causes:
- An unset secret. A workload that declares a secret with no value cannot start.
forge secret list --env <env>shows which are missing; set them withforge secret set. - A workload hosting refuses. Operators, cron-kind workloads, sidecars and volumes cannot run on shared hosting; the render names the workload and field. Bind it to a cluster you operate instead.
- A migration failed. The deploy stops before any new pod serves and prints the command to read the migration’s logs. Fix it with a new migration.
- “cannot authorize”. Your Reliant session lacks deploy permission. See Forge sign-in.
Nothing shows up in Observability
- Local environments have none.
devkeeps its telemetry in the ClickStack on your machine;forge env status devprints the HyperDX URL and checks that traces, metrics and logs are arriving. - Data appears a little after a deploy. The first data arrives once the new pods are serving traffic.
- A category is switched off. Check the environment’s
observabilityblock forenabled = Falseor a signal set toFalse. Profiles and session replay are off on hosting until you turn them on. - The frontend sends nothing. A production build sends browser telemetry only when its OTLP endpoint is configured; a scaffolded frontend deployed to hosting is set up for it.
signals.browser = Falseturns it off. - Alerts never notify. A route names a receiver that is not set up in Observability → Alerts → Receivers; that tab lists routes with no receiver.
- Usage is over the allowance. Past it, an organization without a payment method has debug logs and routine traces dropped. Project settings → Usage shows the state.
Getting more detail
forge project audit summarises the project’s state, including drift between proto and schema and RPCs still returning Unimplemented. forge debug start <service> attaches the Delve debugger to a running service. Agents can load the debug skill, which covers forge’s evidence sources in depth. If forge itself misbehaves, send us feedback or report it on GitHub.