Summary
In a DB graph, updating a :default-typed property value creates a new value block and leaves the previous one behind as a child of the owning block, referenced by nothing. Each subsequent edit adds another. The abandoned blocks are not rendered in the UI (property rows only show the current value), so they accumulate invisibly — one dead entity per property edit, indefinitely.
Verified through the CLI. I have not confirmed whether the desktop UI's property editor takes the same path — that's the main open question below, and it decides whether this is a CLI issue or a core one.
Repro (CLI)
# fresh property + class + instance
logseq upsert property --name repro-note --type default --cardinality one
logseq upsert tag --name repro-thing --add-properties repro-note
logseq upsert block --target-page "Repro Page" --content "subject block"
logseq upsert block --id <block> --update-tags repro-thing
# three edits to the same property
logseq upsert block --id <block> --update-properties '{:user.property/repro-note-XXXX "first value"}'
logseq upsert block --id <block> --update-properties '{:user.property/repro-note-XXXX "second value"}'
logseq upsert block --id <block> --update-properties '{:user.property/repro-note-XXXX "third value"}'
Each write returns a different value-block db/id (8192, 8193, 8194).
Actual
;; children of the owning block
[:find ?c ?t :where [?c :block/parent 8191] [?c :block/title ?t]]
=> [[8192 "first value"] [8193 "second value"] [8194 "third value"]]
;; what the property actually points at
[:find ?v ?t :where [8191 :user.property/repro-note-XXXX ?v] [?v :block/title ?t]]
=> [[8194 "third value"]]
Three children, one referenced. logseq/logseq#8192 and logseq/logseq#8193 are unreachable from any property, and don't appear anywhere in the UI.
Expected
Either the value block is mutated in place, or the abandoned one is reclaimed with the property that stopped pointing at it. A block that no property references and that the UI never renders shouldn't persist in the tree.
Also observed: property deletion
Same shape, larger blast radius. In a real graph, deleting a :default-typed property that 26 instances had values for left 26 orphaned value blocks in place — the property was gone, its value blocks were not. Combined with a second retired property, 30 unreferenced blocks across one container, none visible in the UI.
Why it's hard to notice
The UI renders property values as property rows — "this property → its current value". A value block that no property points at has no row to render in, so it is simply never drawn. The only way to find these is a DataScript query against :block/parent. A user editing normally has no signal that anything is accruing.
Open question / scope
My repro drives the CLI (logseq upsert block --update-properties). I have not verified whether the desktop UI's property editor mutates the existing value block or mints a new one. If the UI mutates in place, this is scoped to the CLI's upsert path rather than to the DB layer. Happy to test that if it's useful — I just don't want to overstate what I've actually confirmed.
Environment
- Logseq CLI — Build time 2026-07-28T15:18:36.673Z, Revision 9a11243
- DB graph (not file-based)
- macOS 26.5.2
Suggested query for anyone auditing an existing graph
Unreferenced value blocks under instances of a class, given that class's :default property idents:
[:find ?child ?title
:where
[?inst :block/tags ?tag] [?tag :db/ident :user.class/YOUR-CLASS]
[?child :block/parent ?inst] [?child :block/title ?title]
(not [?inst :user.property/SOME-DEFAULT-PROP ?child])
(not [?inst :user.property/ANOTHER-DEFAULT-PROP ?child])]
Node-typed properties don't produce these — they point at existing entities, never at child value blocks — so only :default/:number-style properties need listing in the not clauses.
Summary
In a DB graph, updating a
:default-typed property value creates a new value block and leaves the previous one behind as a child of the owning block, referenced by nothing. Each subsequent edit adds another. The abandoned blocks are not rendered in the UI (property rows only show the current value), so they accumulate invisibly — one dead entity per property edit, indefinitely.Verified through the CLI. I have not confirmed whether the desktop UI's property editor takes the same path — that's the main open question below, and it decides whether this is a CLI issue or a core one.
Repro (CLI)
Each write returns a different value-block
db/id(8192, 8193, 8194).Actual
Three children, one referenced. logseq/logseq#8192 and logseq/logseq#8193 are unreachable from any property, and don't appear anywhere in the UI.
Expected
Either the value block is mutated in place, or the abandoned one is reclaimed with the property that stopped pointing at it. A block that no property references and that the UI never renders shouldn't persist in the tree.
Also observed: property deletion
Same shape, larger blast radius. In a real graph, deleting a
:default-typed property that 26 instances had values for left 26 orphaned value blocks in place — the property was gone, its value blocks were not. Combined with a second retired property, 30 unreferenced blocks across one container, none visible in the UI.Why it's hard to notice
The UI renders property values as property rows — "this property → its current value". A value block that no property points at has no row to render in, so it is simply never drawn. The only way to find these is a DataScript query against
:block/parent. A user editing normally has no signal that anything is accruing.Open question / scope
My repro drives the CLI (
logseq upsert block --update-properties). I have not verified whether the desktop UI's property editor mutates the existing value block or mints a new one. If the UI mutates in place, this is scoped to the CLI's upsert path rather than to the DB layer. Happy to test that if it's useful — I just don't want to overstate what I've actually confirmed.Environment
Suggested query for anyone auditing an existing graph
Unreferenced value blocks under instances of a class, given that class's
:defaultproperty idents:Node-typed properties don't produce these — they point at existing entities, never at child value blocks — so only
:default/:number-style properties need listing in thenotclauses.