fix(publish): acquire DO stubs in sync let to avoid RpcPromise clone#12521
Open
deephbz wants to merge 1 commit intologseq:masterfrom
Open
fix(publish): acquire DO stubs in sync let to avoid RpcPromise clone#12521deephbz wants to merge 1 commit intologseq:masterfrom
deephbz wants to merge 1 commit intologseq:masterfrom
Conversation
Under the new cloudflare:workers DurableObject RPC runtime, stubs returned from (.get do-ns do-id) are thenable. Binding them in a (p/let) causes promesa to await the stub, triggering a structured clone that fails with DataCloneError on RpcPromise. Pulling stub acquisition into a surrounding sync (let) avoids the await entirely. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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
Every publish / unpublish / page-view handler in the Cloudflare publish worker fails with a
DataCloneErrorbecause promesa'sp/letawaits Durable Object stubs that are thenable under the newcloudflare:workersRPC runtime. Moving stub acquisition into a surrounding syncletfixes the whole worker.How I hit this
I'm running a self-hosted copy of this publish worker (own CF account, own R2 bucket, own
PublishMetaDO) deployed fresh from the currentmasterbuild. Every request against the worker failed:POST /pagesreturned500withDataCloneError: Could not serialize object of type \"RpcPromise\".GET /page/:graph/:pagereturned500with the same error.DELETE /pages/:graph/:pagesame.The desktop client showed the matching error toast after clicking Publish page. Worker tail logs had the
RpcPromiseclone error on every handler that touchesPUBLISH_META_DO.Why the official worker isn't affected (hypothesis)
The production
logseq-publishworker atlogseq.iowas deployed from a pre-cljs JS bundle (the generation beforedeps/publish/was ported to ClojureScript). That older code acquired the DO stub synchronously:When the codebase was rewritten in cljs (
deps/publish/src/logseq/publish/routes.cljs), the stub acquisition was pulled intop/letbindings alongside thefetchcalls. Tests presumably pass because test DOs aren't thenable in the same way. But the official worker apparently hasn't been redeployed from the cljs bundle yet, so production keeps running the old JS and never trips the new bug.Once someone fresh-deploys the current cljs source (which is what I did), every handler explodes.
Root cause
In
deps/db-sync's RPC-enabled Durable Object (extends DurableObjectfromcloudflare:workers), the stub returned byDurableObjectNamespace.get(id)is a thenable (it exposes a.thenmethod so callers canawaitan RPC return value). When this stub is bound inside a(p/let ...):promesa sees
do-stubis thenable and awaits it. Awaiting a DO stub is not a documented operation; the runtime tries to structure-clone it and fails withDataCloneError: Could not serialize object of type \"RpcPromise\".Fix
Acquire the stub in a surrounding sync
letso promesa never awaits it, then use it normally insidep/let:Applied to every handler in
routes.cljsthat touchesPUBLISH_META_DO: page GET/POST/DELETE, tag page handlers, password hash fetch, index rebuild, etc. Thefetch-page-password-hashhelper carries a short comment explaining the pattern so future contributors don't re-inline the stub intop/let.Local repro (before)
cd deps/publish && yarn install && yarn releasewrangler deployto any fresh CF account with aPublishMetaDODO binding.500, worker tail logsDataCloneError: Could not serialize object of type \"RpcPromise\", no R2 object written, no DO row inserted.Also reproducible via plain HTTP:
After the fix
Same worker, redeployed with this patch:
POST /pagesreturns200with the public URL.GET /page/:graph/:pagereturns SSR HTML.DELETE /pages/:graph/:pagereturns200and purges both the R2 transit blob and the DO row.logseq.property.publish/published-urlon the page.just personal-publish-tailis silent, no moreRpcPromiseerrors.Test plan
lseek-publish-createdat20260418worker from this branch; publish / view / unpublish all succeed end-to-end.DataCloneErroris gone from worker tail logs across all three request types.deps/publishhas no handler-level tests today; behaviour verified live against a real CF worker + R2 + DO).🤖 Generated with Claude Code