Revert PR #52 "fix(executor): use real Fetcher for owner-id outbound wrapper"#53
Merged
jonnyparris merged 1 commit intomainfrom Apr 27, 2026
Merged
Conversation
…#52)" This reverts commit b52345f. The LOADER.get() approach swapped one error for another. The wrapper worker stub returned by getEntrypoint() cannot be transferred across the boundary into another LOADER-spawned Worker (the codemode sandbox). Runtime error from inside codemode fetch(): Entrypoints to dynamically-loaded workers cannot be transferred to other Workers, because the system does not know how to reload this Worker from scratch. Instead, have the parent Worker expose an entrypoint which constructs the dynamic worker and forwards to it. Net effect: outbound fetch() from codemode is still broken, just at a later stage. Reverting to the duck-typed wrapper while a proper fix (likely a real WorkerEntrypoint subclass with per-call props, or moving the owner-id channel out of the request entirely) is designed.
jonnyparris
added a commit
that referenced
this pull request
Apr 27, 2026
… props (#54) Replaces the broken `wrapOutboundWithOwner` wrapper with workerd's native per-call props mechanism on the OUTBOUND self-binding. ## The two failed attempts before this 1. Original duck-typed wrapper: returned `{ fetch: ... } as Fetcher` for `globalOutbound`. Workerd validates that globalOutbound must be a real ServiceStub and rejected it at executor instantiation: 'Incorrect type for the globalOutbound field on WorkerCode...' 2. PR #52 / LOADER.get() wrapper: produced a real Fetcher via dynamic loader, but that stub was non-transferable across the LOADER.get() boundary into codemode's sandbox. Live error from inside codemode: 'Entrypoints to dynamically-loaded workers cannot be transferred to other Workers...' Reverted in PR #53. ## The fix workerd actually wants `OUTBOUND` is already declared as a self-binding to AllowlistOutbound in wrangler.jsonc, which makes it a `LoopbackServiceStub`. Loopback stubs are callable: `env.OUTBOUND({ props: { ownerId } })` returns a real, transferable Fetcher with per-call props attached. The receiving WorkerEntrypoint reads them from `this.ctx.props` — which the sandbox cannot tamper with, since props ride workerd's RPC channel, not the request body. ## Changes - `src/types.ts`: type `OUTBOUND` as `LoopbackServiceStub<AllowlistOutbound>` so the callable signature is visible to TS and `env.OUTBOUND({ props })` typechecks. - `src/outbound.ts`: AllowlistOutbound now extends `WorkerEntrypoint<Env, AllowlistOutboundProps>`. `fetch()` reads `this.ctx.props?.ownerId` instead of an injected request header. `OWNER_ID_HEADER` constant deleted. - `src/executor.ts`: `wrapOutboundWithOwner` (LOADER.get attempt) replaced with `bindOutboundWithOwner` — a one-liner that calls `outbound({ props: { ownerId } })`. No wrapper, no extra worker. - `src/agentic.ts`: same call-site updated. - `test/outbound.test.ts`: 4 new unit tests for the producer side (passthrough cases, props injection, no cross-mixing per ownerId). 643 → 647 tests passing. ## Migration safety If only one side of the change deploys (e.g. mid-rollout), the worst case is that AllowlistOutbound sees no ownerId and falls back to no per-user auth — the same behaviour as the broken state we're leaving. No regressions are introduced.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reverts #52. The fix swapped one runtime error for another.
What happened
The
LOADER.get()approach returned a realFetcher(good — workerd accepted it asglobalOutbound), but that stub cannot be transferred across the boundary into the codemode-loaded sandbox Worker. Runtime error from inside any codemodefetch():This was confirmed live after deploying
b52345fto production by runningawait fetch(...)inside a fresh codemode session.Net effect
Outbound
fetch()from codemode is still broken — just at a later stage. The original duck-typed wrapper at least failed at executor instantiation; this version defers the error to actual fetch time, which is arguably worse for diagnosis.Path forward
A proper fix needs to satisfy two constraints workerd enforces:
globalOutboundmust be a realFetcher(rules out the duck-typed object)LOADER.get()boundaries (rules out anotherLOADER.get()stub)Likely options:
WorkerEntrypointclass statically declared inwrangler.jsonc(e.g.OutboundForwarder) that wrapsOUTBOUNDand uses workerd per-call props for the per-request ownerIdBoth need real design work and tests; punting until properly thought through.
🤖 generated by ruskin's agent