fix(worker): allow a missing :block/order on a direct child - #13081
Open
ChipNowacek wants to merge 1 commit into
Open
fix(worker): allow a missing :block/order on a direct child#13081ChipNowacek wants to merge 1 commit into
ChipNowacek wants to merge 1 commit into
Conversation
direct-children-membership rejects any direct child whose :block/order
is not a string, and the rejection is a throw that discards the whole
membership read rather than the one child. Because open-block-tree
calls it recursively over the open tree, a single such child makes
every ancestor page unopenable -- the throw returns to the renderer as
a 500 from the worker and is caught by the React ErrorBoundary, so the
user gets "Something went wrong" with no indication of which page or
which block is involved, though the ex-info already carries both.
The rejected value is legal. :block/order is {:optional true} for pages
in normal-page, `graph validate` reports nothing against such a page,
and the pre-worker renderer displayed it without complaint. Only
block-attrs makes the attribute mandatory, and that governs normal
blocks; a page nested under another page may carry none.
Nothing downstream needs the guard either. The very next expression is
(sort-by second), and cljs.core/compare already orders nil ahead of any
string -- which is what ldb/sort-by-order, a bare (sort-by :block/order),
relies on everywhere else in the codebase. So accept nil and let it
sort first, consistent with the rest of the tree, while still rejecting
a non-string value as the real defect it would be.
Hit on a live DB graph: one page created without an order made the
top-level Library page, four levels above it, impossible to open.
Adds tests for both halves -- an order-less child no longer removes its
siblings, and a numeric order still throws. The first fails on the old
implementation with the same error seen in the wild.
tiensonqin
self-requested a review
August 21, 2026 08:00
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.
Why this is a PR and not an issue
Filing it as a patch because a patch is the most specific statement of the
problem I can make — it names the exact line, the exact value, and what I think
should happen instead, with tests that pin both halves.
Please close it without ceremony if it isn't useful. I'm not looking to
defend this. If the guard is deliberate, if this area is mid-rewrite, or if you'd
rather solve it a different way, throwing it away costs me nothing and the
report below still stands on its own. I'd rather hand you something concrete and
disposable than something vague you have to interrogate.
What happens
Opening a page whose open subtree contains a direct child with no
:block/orderfails the whole page:
The throw is in
direct-children-membership(
src/main/frontend/worker/handler/block.cljs:219), reaches the renderer as a500 from the worker, and is caught by the React ErrorBoundary — so the user gets
"Something went wrong" and the app is unusable until relaunch.
Because
open-block-treecallsdirect-children-membershiprecursively, thepage that fails can be far above the block that causes it. On the graph where I
hit this, one page four levels down made the top-level Library page
impossible to open.
Why I think the rejected value is legal
:block/orderis{:optional true}for pages:It is required for normal blocks and optional for pages. The block in
question is a page (
:block/name, tagged:logseq.class/Page) nested underanother page, which this graph does for its own hierarchy.
Corroborating that reading:
logseq graph validatescoped to the page returns{:pages_scanned 1, :blocks_scanned 4, :findings []}.9a11243) renders the same graph fine.Only the post-refactor build (rev
71db2e3) fails.— same tags, same parent, same shape. The order is the only difference.
Why the guard doesn't seem load-bearing
The very next expression is
(sort-by second), andcljs.core/comparealreadyorders
nilahead of any string. That is exactly whatldb/sort-by-order— abare
(sort-by :block/order blocks)— relies on everywhere else in the tree.So the surrounding code already tolerates what this guard rejects.
The patch accepts
niland lets it sort first, while still rejecting anon-string value, which would be a real defect:
Tests
direct-children-membership-allows-missing-child-order-test— an order-lesschild no longer removes its siblings from the result, and sorts first.
direct-children-membership-rejects-non-string-child-order-test— a numericorder still throws.
Verified against
masterby reverting the one-line source change and rerunningthe namespace: the first test errors with the same message seen in the wild,
and passes with it. Full namespace: 16 tests, 100 assertions, 0 failures.
No regressions across every
frontend.worker.*andfrontend.db.*namespace —1071 tests, 4989 assertions, 0 failures, 0 errors.
Deliberately not in this PR
Three things I noticed but didn't touch, because they're design calls that are
yours to make:
fail-render-read!sites in this file. Failing an entire page's read because one child is odd is
a large blast radius for a render path; skipping the child and rendering the
rest is the other obvious design. I only changed the predicate.
ex-infoalready carries:parent-uuid,:block-uuidand:block-order, and none of it reaches theuser. "Invalid direct-child order" alone is hard to act on.
fix the block from anywhere: the UI can't render the page that holds it,
logseq upsert blockrefuses page sources withinvalid-source: source must be a non-page block, andlogseq.Editor.moveBlocksilently no-ops on a page(returns
null, changes nothing, raises nothing — the same call on a normalblock works). I repaired it by dragging the page in an older build. That
silent
moveBlockno-op may be worth its own issue; say the word and I'llopen one.
What I could not determine
What wrote a page with no
:block/orderin the first place. It was created2026-08-02; the graph's
client-opslog only begins 2026-08-07, so it can't beinspected. Every ordinary write path I can test on the current build assigns an
order correctly — creating a page under a parent, and even recycling one. So I
can't tell you whether there's an upstream write bug here or a one-off from an
older build. If that origin matters more to you than the read-path behaviour,
I'm happy to dig further.
Environment
Logseq DB graph, desktop macOS arm64, app 2.0.1, build rev
71db2e3.Not reproducible on rev
9a11243. Present onmasterat047acf36ee.