You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
os serve <config> cannot boot without a prebuilt dist/objectstack.json — dies with Service 'manifest' is async - use await #4085
Found while fixing #4012 (PR #4084), where the regression test needed a temp project booted through the CLI. Unrelated to that fix, so filing rather than expanding scope (AGENTS.md Prime Directive #10).
Symptom
os serve objectstack.config.ts fails during boot whenever dist/objectstack.json is absent beside the config:
Loading objectstack.config.ts...
[StandaloneStack] artifact read FAILED: path='<cwd>/dist/objectstack.json' error=ENOENT: no such file or directory
✗ Service 'manifest' is async - use await
Exit code 1. The thrower is packages/core/src/kernel.ts:106 — getService finds the service in the plugin loader as an unresolved Promise and refuses it.
Repro — it is not about the fixture being minimal
The clean control is examples/app-todo, which boots fine normally:
cd examples/app-todo
mv dist/objectstack.json /tmp/ # the ONLY change
os serve objectstack.config.ts --port 44318
# ✗ Service 'manifest' is async - use await → exit 1
mv /tmp/objectstack.json dist/ # restore
os serve objectstack.config.ts --port 44318
# ✓ Server is ready
So a known-good app fails purely because its compiled artifact is missing. Any freshly-authored project — os init and then os serve before a first os compile — hits the same wall.
The earlier warning
With #4084's boot-diagnostics fix applied, the preceding boot WARN is now visible and points at the same root:
WARN [AppPlugin] failed to seed persisted package state {"error":"Service 'objectql' is async - use await"}
AppPlugin reaches for objectql (and later manifest) while those services are still async factories mid-resolution. With the artifact present something else resolves them first and the race never opens. Worth noting that this warning was itself invisible before #4084 — the failure gave no clue as to why.
Why it matters
Config-boot is a documented, first-class path — serve prints Loading objectstack.config.ts... and its own not-found hint offers objectstack build as one option among others, not a prerequisite. The error message names neither the missing artifact nor the fix, so it reads as an internal fault rather than "run os compile first".
What "fixed" looks like
Either AppPlugin awaits the services it depends on (so no artifact is required and config-boot works as documented), or — if a prebuilt artifact really is mandatory — serve should detect the absent artifact up front and refuse with a message that says so and names os compile.
Workaround
Run os compile before os serve. (That is what #4084's e2e fixture does.)
Checked whether this shares a root cause with #4095 (PR #4129), since both live on the config-boot path and looked like they might be one bug. They are not — posting the verdict so nobody re-derives it.
The two are opposite sides of the same fork in serve.ts, and which side you land on is decided purely by whether dist/objectstack.json exists:
Verified rather than reasoned: with #4129's fix applied, moving examples/app-todo/dist/objectstack.json aside still fails identically.
✗ Service 'manifest' is async - use await
So this stays open and independent. One data point that may help whoever picks it up: the preceding boot WARN, visible since #4012, is
WARN [AppPlugin] failed to seed persisted package state {"error":"Service 'objectql' is async - use await"}
— i.e. AppPlugin reaches for objectql (and later manifest) while those are still unresolved async factories. With the artifact present, something resolves them before AppPlugin.start() runs and the race never opens; on the wrap-the-config path it does. That points at ordering/awaiting in the wrap path rather than at the services themselves.
Activity
fix(cli): a config whose app cannot be registered says so instead of …
os-zhuang commentedon Jul 30, 2026
Checked whether this shares a root cause with #4095 (PR #4129), since both live on the config-boot path and looked like they might be one bug. They are not — posting the verdict so nobody re-derives it.
The two are opposite sides of the same fork in
serve.ts, and which side you land on is decided purely by whetherdist/objectstack.jsonexists:createStandaloneStack()returns an artifact-derivedAppPlugin, so serve'shasAppPluginAlreadyguard is true and it skipsnew AppPlugin(config). Boot succeeds; the module'sonEnableis dropped, so handlers never register. That is Config-booted apps lose theironEnablehook — every script action handler goes unregistered and 404s at dispatch (examples/app-todo: all 8) #4095, fixed in fix(cli): a config-booted app keeps its module code — action handlers reach the engine again (#4095) #4129.AppPlugin,hasAppPluginAlreadyis false, serve does wrap the config — and the boot then dies here withService 'manifest' is async - use await. That is this issue, and it is reached by a different code path than the one fix(cli): a config-booted app keeps its module code — action handlers reach the engine again (#4095) #4129 touches.Verified rather than reasoned: with #4129's fix applied, moving
examples/app-todo/dist/objectstack.jsonaside still fails identically.So this stays open and independent. One data point that may help whoever picks it up: the preceding boot WARN, visible since #4012, is
— i.e.
AppPluginreaches forobjectql(and latermanifest) while those are still unresolved async factories. With the artifact present, something resolves them beforeAppPlugin.start()runs and the race never opens; on the wrap-the-config path it does. That points at ordering/awaiting in the wrap path rather than at the services themselves.Generated by Claude Code
fix(cli,core,metadata,runtime): the platform boots with no applicatio…
test(cli): move the #4095 e2e onto the shared serve harness main intr…
fix(cli,runtime): a NAMED artifact that is missing is a broken instru…
fix(cli,runtime): a named artifact that is missing fails loudly, and …
feat(core,runtime): plugin ordering is a declared, kernel-enforced co…
test(cli): pin serve config-boot for the authored shape #3887 reported (
feat(core): enforce the init-service declaration contract with a CI g…