When a <script client> block fails to bundle, stx logs a console.warn, ships the unbundled source to the browser, and the build exits 0. On the dev server this app actually runs, the failure is invisible in the browser by construction.
The fallback
src/client-script-bundler.ts:619-621:
console.warn(`[stx:bundler] error: ${formatBuildFailure(error, filePath || undefined)}`)
return code // fall back to the original, unbundled sourceNothing increments a failure counter. ssg.ts:1165 result.failedCount++ is reachable only from the catch at :1164, and bin/cli.ts:1139-1141 exits 1 only when result.ssg.failedCount > 0. So stx build reports success.
The warning is also suppressed by default — shouldLogBundlerDiagnostics (:27-31) gates on STX_DEBUG === 'true' | '1'.
There's no config switch to make it fatal: StrictModeConfig (types/config-types.ts:393-400) has only enabled, failOnViolation, allowPatterns, and its sole consumer is validateClientScript for prohibited DOM patterns (process.ts:1799).
In the browser it's worse
The error overlay in hot-reload.ts:292-321 is a connection-status overlay. Its one error branch (:370-373, case 'error': showOverlay('Build Error: ' + msg.content, true)) has exactly one producer anywhere — dev-server/serve-file.ts:569, hmrServer.error('Build failed - check console for details') — a generic string with no file, line, column or code frame, on the single-file server.
But a Stacks app doesn't run that server. It runs bun-plugin-stx (@stacksjs/actions/dist/dev/views.js:60 → import('bun-plugin-stx/serve')), whose HMR client (bun-plugin-stx/src/serve.ts:987) handles only m.type === 'reload' and m.type === 'css'. There is no error channel at all.
So the end-to-end experience is: the page loads, the script is present but unbundled, imports don't resolve, bindings silently do nothing, the terminal is quiet unless STX_DEBUG is set, and the build exits 0.
Why it matters
This class of silent degradation costs a lot of time to diagnose, because every symptom points somewhere else. Chasing an unrelated "component renders nothing" problem in this app took roughly thirty probes before the cause turned out to be in the build path rather than the template — the page rendered, the markup was correct, and nothing anywhere said a build step had failed.
To stx's credit the diagnostic data is already in hand: formatBuildFailure (client-script-bundler.ts:610-618) recovers line and column and rewrites the .stx/bundle-tmp path back to the source view. Only the transport and the fatal-exit path are missing.
Ask
stx build: a bundler failure should incrementfailedCountand exit non-zero. Shipping unbundled source as a success is the wrong default.stx dev: a full-screen overlay with file, line, column, code frame and the original error, mapped back to the.stxsource — and an error channel inbun-plugin-stx/serve, which currently has none.
✖ resources/views/composer.stx:412:9 Could not resolve "./missing"
410 | const x = 1
411 |
> 412 | import { thing } from './missing'- A
strict: { bundlerFallback: 'error' }switch so silent degradation is opt-in rather than the default.
Maps to Vite's full-screen HMR error overlay plus a non-zero vite build exit.
Environment
@stacksjs/stx 0.2.153, bun-plugin-stx dev server, Bun 1.3.1.