Notion: guard against entity_id-as-title mistakes, fix stale relation resolution after entity_id correction - #89
Merged
Conversation
…, same pattern as statusMarkerBlock
…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.
…h notion_update_page
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.
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_pagerequired eitherentity_idorone_off: true, but nothing stopped someone from typing the intended entity_id string intotitlewhile leavingentity_idunset. 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:
doCreatePagenow rejects titles matching a slug-like shape (SLUG_LIKE_TITLE: lowercase/digits, 3+ hyphen-joined segments) wheneverentity_idis unset — regardless ofone_off, since the observed orphans happened withone_offboth set and omitted. The error tells the caller to pass the string asentity_idinstead. A deliberate literal slug title is still possible by passingentity_idexplicitly.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 andnotion_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 vianotion_update_page's genericreplacements, 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_idparam tonotion_update_page/notion_update_pages_batch/doUpdatePagethat updates the marker block and upserts the Entity Index row in the same call — mirroring howstatusalready round-trips through its own marker block.entityMarkerBlockadded toclient.jsalongside the existingstatusMarkerBlock.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— exportentityMarkerBlockconnectors/notion/tools.js—SLUG_LIKE_TITLEguard indoCreatePage;entity_idupdate+reindex path indoUpdatePage; schema/description updates onnotion_update_pageandnotion_update_pages_batchNo existing Notion test file in
test/to update — flagging that this connector currently has no automated coverage, if that's worth a follow-up.