Bug: page update mutation without tags half-applies the update — content is saved, render/search/storage are skipped, then the API reports failure
#8022
Unanswered
ferr079
asked this question in
Error / Bug Report
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
Calling the GraphQL
pages.updatemutation without the optionaltagsargument crashes mid-pipeline with:The schema declares
tags: [String]as optional onupdate(page.graphql), so this is a legal call per the API contract — but the implementation throws. The error itself is old news (#2702, closed in 2021 with a "pass tags" workaround). What I haven't seen reported is the dangerous part:The update is half-applied. The raw content is committed to the DB before the crash, but everything after the crash point is skipped. The API then tells the client the update failed.
Observed consequences
When the mutation is called without
tags:content,title,description, etc. are saved (thepatch()runs first)renderPageis skipped → the page serves the stale rendered HTML (viewers don't see the new content){ succeeded: false, message: "Cannot read properties of undefined (reading 'map')" }Point 5 is the trap for API automation: a client that trusts the response and retries the update (e.g. a script doing read-modify-write with appends) will apply its change twice, because the first attempt actually persisted. That's how I found this — an automation almost double-appended content after treating the response as a clean failure.
This also leaves the page in the same family of inconsistent states as #7986 (
renderout of sync withcontent): the page pipeline is not atomic, and a mid-pipeline throw strands the page halfway.Minimal reproduction
→ Response:
{"succeeded": false, "message": "Cannot read properties of undefined (reading 'map')"}→ DB check:
contentwas updated anyway;renderstill holds the previous version.Reproduced on 2.5.x (PostgreSQL). The code path is unchanged on
mainas of6f042e9.Root cause
server/models/pages.js—updatePage:server/models/tags.js—associateTags:Interestingly,
createPagedoes guard this same call (pages.js#L328):updatePagejust misses the equivalent guard.Suggested fix
Mirror the
createPageguard inupdatePage, with update semantics:tagsomitted/undefined → leave existing tags untouched (consistent with how other optional fields behave on update)tags: []explicitly passed →associateTagsalready handles unrelating all tagsA defensive
tags = tags || []at the top ofassociateTagswould also protect any other caller. Longer term, wrapping the update pipeline (patch → tags → render → search → storage) in a transaction would prevent this entire class of half-applied updates — but the one-line guard fixes the reachable crash today.Workaround for API users
Always pass
tagsexplicitly onupdate(fetch the current tags first viapages.single(id) { tags { tag } }and echo them back). And do not trustsucceeded: falseto mean nothing was written — verify before retrying.Beta Was this translation helpful? Give feedback.
All reactions