Skip to content

Notion: guard against entity_id-as-title mistakes, fix stale relation resolution after entity_id correction - #89

Merged
allocsys merged 8 commits into
mainfrom
fix/notion-entity-id-guard-and-reindex
Aug 7, 2026
Merged

Notion: guard against entity_id-as-title mistakes, fix stale relation resolution after entity_id correction#89
allocsys merged 8 commits into
mainfrom
fix/notion-entity-id-guard-and-reindex

Conversation

@allocsys

@allocsys allocsys commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Fixes the two data-integrity gaps found in a workspace audit (see handoff bug report).

Bug 1 — no guard against entity_id-as-title mistakes

notion_create_page required either entity_id or one_off: true, but nothing stopped someone from typing the intended entity_id string into title while leaving entity_id unset. This produced 6 confirmed empty orphan pages (madmcp-cloudflare-workers-migration-plan, joblead-liliana-model-n8n, jobreq-liliana-model-n8n-detail, candidate-release-plz-release-plz-2130, laborx-scenium-94796, madmcp-generate-lockfile-workflow-recreation-2026-07-26).

Fix: doCreatePage now rejects titles matching a slug-like shape (SLUG_LIKE_TITLE: lowercase/digits, 3+ hyphen-joined segments) whenever entity_id is unset — regardless of one_off, since the observed orphans happened with one_off both set and omitted. The error tells the caller to pass the string as entity_id instead. A deliberate literal slug title is still possible by passing entity_id explicitly.

Chose the "reject slug-shaped titles" option from the bug report rather than a collision-lookup or docs-only fix, since it catches the mistake at the moment it happens instead of relying on a later audit, and doesn't require an extra dedup-index round trip on every create.

Bug 2 — relation resolver reports "not found" for entity_ids that do exist

Root cause: findPageByEntityId (used both for create-time dedup and notion_get_page's relation resolution) reads from the separate Entity Index database — not from the live marker block text. There was no supported way to change an entity_id after page creation; the only option was hand-editing the 🔑 entity_id: ... marker block via notion_update_page's generic replacements, which patches the visible block only and never touches the index. So after "fixing" a mismatched entity_id that way, the index kept pointing at the old value (or nothing), and any relation pointing at the corrected entity_id kept resolving as dangling.

Fix: added a dedicated entity_id param to notion_update_page / notion_update_pages_batch / doUpdatePage that updates the marker block and upserts the Entity Index row in the same call — mirroring how status already round-trips through its own marker block. entityMarkerBlock added to client.js alongside the existing statusMarkerBlock.

Known limitation (documented in code): this doesn't delete the old entity_id's stale index row if the page had one — best-effort, same tradeoff the rest of this file already accepts elsewhere (e.g. appendIndexEntry's own error handling). The bug this fixes is specifically that lookups against the corrected entity_id were failing; those now succeed immediately.

Files changed

  • connectors/notion/client.js — export entityMarkerBlock
  • connectors/notion/tools.jsSLUG_LIKE_TITLE guard in doCreatePage; entity_id update+reindex path in doUpdatePage; schema/description updates on notion_update_page and notion_update_pages_batch

No existing Notion test file in test/ to update — flagging that this connector currently has no automated coverage, if that's worth a follow-up.

…bug 1)

Fixes 6 confirmed empty orphan pages where the caller typed the intended
entity_id string into `title` and left `entity_id` unset. The existing
"entity_id or one_off" guard didn't catch this because one_off:true (or an
accidental omission that still isn't caught) satisfies it either way -- the
actual mistake is a slug-shaped title with no entity_id, regardless of how
one_off was set. This guard fires on that shape specifically and tells the
caller what they probably meant.
…(bug 2)

Fixes relation targets resolving as "(not found -- dangling reference)"
even when a page with the corrected entity_id genuinely exists. Root cause:
findPageByEntityId (used both for create-time dedup and notion_get_page's
relation resolution) reads from the separate Entity Index database, not
from the live marker block text -- but there was no supported way to change
an entity_id after page creation. The only way to "fix a mismatched
entity_id" was to hand-edit the marker paragraph via notion_update_page's
generic `replacements`, which patches the visible block only and never
touches the index -- so the index kept pointing at the old value (or
nothing), and every relation-target lookup against the corrected entity_id
kept missing. This adds a dedicated entity_id param that updates the marker
block AND upserts the index entry in the same call, mirroring how `status`
already round-trips through its own marker block.
@allocsys
allocsys merged commit f16e4fe into main Aug 7, 2026
2 checks passed
@allocsys
allocsys deleted the fix/notion-entity-id-guard-and-reindex branch August 7, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant