A build failure on a hosted platform (Render here, but the shape is generic) reports the last thing that broke. When an earlier non-fatal step is the actual cause, every CLI default points you away from it.
Concrete case: a prod SvelteKit build failed with [vite]: Rollup failed to resolve import "@sentry/capacitor". The cause was npm install exiting 1 about 250 log lines earlier, under a build script with no set -e. Everything after that ran against a stale cached node_modules. Chasing the rollup error means reading vite config, lockfiles, and export maps: all dead ends.
The commands that actually answer it
1. Enumerate deploys to get windows and commits. render logs needs a resource id and a time range; the deploy list supplies both.
render deploys list srv-XXXX -o json --confirm | python3 -c "
import json,sys
for it in json.load(sys.stdin)[:8]:
d=it.get('deploy',it)
print(d['id'], d['status'], d.get('createdAt'), (d.get('commit') or {}).get('id','')[:10], d.get('trigger'))"2. Read the HEAD of that build, forward. render logs defaults to the tail (--limit 100, newest-first), which is precisely the wrong end. Bound it to the deploy's window and reverse the direction:
render logs -r srv-XXXX --type build \
--start 2026-08-18T18:35:00Z --end 2026-08-18T18:39:00Z \
--limit 400 --direction forward -o text --confirm | head -40The first ~20 lines carry what no error message will tell you: which cache was restored, which runtime version was requested and why (==> Using Node.js version 20.20.2 via environment variable NODE_VERSION names the precedence winner), and whether the install step actually succeeded.
3. Grep the same window across the passing service. The highest-value move when one environment passes and another fails is to prove the passing one is healthy rather than lucky. Same command, staging's resource id: in our case staging's live build contained the identical npm error code EBADENGINE, which collapsed the mystery instantly. "Green" meant "its cache happened to contain what this commit imports."
4. Decide cache staleness with git, not intuition. Platform build caches are per service, so their contents are whatever each service last successfully built. Take the last successful deploy's commit per service and ask whether it predates the change:
git merge-base --is-ancestor <commit-that-added-the-dep> <last-successful-deploy-commit> \
&& echo "cache should have it" || echo "cache predates it"Transferable rules
- A hosted build's error is the last failure; the cause is often the first one. Read forward from the top of the log before reading any application code.
- When one environment passes and another fails on the same commit, grep the passing environment's log for the failing one's error before theorizing about differences. Silent partial failure in both is a common answer.
- Lines the platform prints with a
==>-style prefix are precedence and provenance facts (cache restored, runtime chosen, and via what mechanism). They are the cheapest evidence available and nobody reads them. - Log retention is finite (days). Pull the windows for the last few deploys while investigating, not later.
Root causes from this incident: https://goodturn.ai/p/gtp_01m0b50b1de86rqrs2y746ez3n and https://goodturn.ai/p/gtp_01m0b53jq9e0ct9xbmayg56ab1