Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(cli): don't apply upsert block tags/properties to referenced pages - #13129

Open
dalxds wants to merge 1 commit into
logseq:masterfrom
dalxds:fix/cli-upsert-block-ref-page-contamination
Open

fix(cli): don't apply upsert block tags/properties to referenced pages#13129
dalxds wants to merge 1 commit into
logseq:masterfrom
dalxds:fix/cli-upsert-block-ref-page-contamination

Conversation

@dalxds

@dalxds dalxds commented Aug 27, 2026

Copy link
Copy Markdown

Summary

upsert block in create mode applies --update-tags and --update-properties to pages referenced from the block's content, not just to the block being created.

Creating a block that links a page silently tags that page and overwrites properties on it. In a scripted graph build this is data loss: every page linked from block content picked up the block's tags, and shared property names (source, in my case) were overwritten on pages that already had a correct value.

Reproduction

logseq graph create --graph repro
logseq upsert tag      --graph repro --name MyTag
logseq upsert property --graph repro --name my-prop --type default --cardinality one
logseq upsert page     --graph repro --page Target
logseq upsert page     --graph repro --page Home

logseq upsert block --graph repro --target-page Home \
  --content 'Mentions [[Target]]' \
  --update-tags '["MyTag"]' \
  --update-properties '{:user.property/my-prop-XXXX "block-only"}'

logseq show --graph repro --page Target

Actual — the referenced page carries the block's tag and property:

195 Target #Page #MyTag
    my-prop: block-only

Expected: Target is unchanged; only the new block carries them.

The referenced page does not need to pre-exist — a page auto-created by the reference is affected the same way. Confirming both entities are written together:

logseq query --graph repro \
  --query '[:find ?e ?a :in $ ?tx :where [?e ?a _ ?tx]]' --inputs '[<tx>]'
# => [[195 "block/tags"] [195 "block/updated-at"]
#     [213 "block/tags"] [213 "block/updated-at"]]

One batch-set-property transaction, both entities — so the page id reached batch-set-property! as a target.

Root cause

execute_create_block (cli/lib/upsert.ml) applies the resolved update plan to the ids returned by Add.execute_add_block:

| Some ids ->
    bind (resolve_block_uuids_by_id invoke_config action.repo ids) (function
      | Ok block_uuids ->
          bind (apply_resolved_update_plan invoke_config action.repo
                  block_uuids resolved_plan) ...

Those ids come from Add.resolve_created_ids, which prefers uuids collected from the insert transaction's tx-data:

let resolve_created_ids config repo blocks insert_result =
  let uuids = collect_created_uuids (tx_data insert_result) in
  if Vec.is_empty uuids then collect_action_block_uuids blocks ...

Since 2d1f17ee2b ("fix: add CLI block page refs"), the insert payload carries :block/refs entity maps for [[page]] references, so the insert transaction creates or touches those pages and their uuids land in tx-data. The update plan is then applied to the caller's block and to every page it references.

The scope is limited to this call site. execute_task_create in the same module already does the right thing, and the asset path only returns the ids without applying an update plan.

Fix

Target the action's own block uuids, falling back to id resolution only when the action carries none — the pattern execute_task_create already uses:

| Some ids ->
    let block_uuids = block_uuids_of_add_action add_action in
    let resolved_uuids =
      if Vec.is_empty block_uuids then
        resolve_block_uuids_by_id invoke_config action.repo ids
      else pure (Ok block_uuids)
    in
    bind resolved_uuids (function ...

Tests

Added block-upsert-update-tags-not-applied-to-referenced-page-json to cli-e2e/spec/non_sync_cases.edn. It creates a block linking a page with both --update-tags and --update-properties, then asserts list node --tags returns exactly one node — the block — with no second (page) entry. The case fails before this change and passes after.

Also recorded the bug in docs/cli/ocaml-cli-bugs-and-fixes.md, matching the existing entry format.

Not fixed here

upsert block still reports referenced pages as created blocks:

$ logseq upsert block --graph repro --target-page Home --content 'x [[Target]]' --output json
{"status":"ok","data":{"result":[213,195]}}   # 195 is the referenced page

That comes from the same resolve_created_ids tx-data behaviour and feeds the bug above. It looked like a separate, wider-blast-radius change (the ids are returned to callers and asserted in existing specs), so I left it out to keep this PR focused on the data-corruption path. Happy to follow up if you'd like it addressed.

Notes for reviewers

  • I don't have an OCaml toolchain on this machine, so the change is not compiled or dune fmt-checked locally — it needs CI. It's structurally identical to the working execute_task_create code a few hundred lines above, and lines stay within the 80-column ocamlformat margin.
  • Found while scripting a graph build through the CLI; the CLI shipped in Logseq 2.0.1 (cli/lib/add.ml is byte-identical to master) reproduces it.

`upsert block` in create mode applied `--update-tags` and `--update-properties`
to the ids returned by `Add.execute_add_block`. Those ids come from
`resolve_created_ids`, which reads the insert transaction's `tx-data` and so
includes pages the transaction created or touched to satisfy `[[page]]`
references in the block content — not just the blocks the caller asked to
create.

Creating a block whose content links a page therefore stamped the block's tags
and properties onto that page as well, overwriting any shared property (for
example `source`) it already had. Both entities were written in a single
`batch-set-property` transaction, so the page silently gained the tag.

Target the action's own block uuids instead, falling back to id resolution only
when the action carries none. This is the pattern `execute_task_create` in the
same module already uses.
@CLAassistant

CLAassistant commented Aug 27, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants