Skip to content

🐛 Fixed "linked comment is no longer available" error after unhiding a comment#28493

Merged
jonatansberg merged 1 commit into
mainfrom
fix-comment-read-cache-invalidation
Jun 11, 2026
Merged

🐛 Fixed "linked comment is no longer available" error after unhiding a comment#28493
jonatansberg merged 1 commit into
mainfrom
fix-comment-read-cache-invalidation

Conversation

@luissazevedo

Copy link
Copy Markdown
Contributor

Problem

Unhiding a comment in admin and then using "View on post" can land on the post with the comments section showing "The linked comment is no longer available." instead of scrolling to the comment.

The permalink flow in comments-ui (fetchScrollTarget) loads the target via GET /api/members/comments/:id/ and requires status === 'published'. On hosts that cache the members API at the edge (purged via the X-Cache-Invalidate header), every comment mutation purged only:

  • /api/members/comments/post/<postId>/
  • /api/members/comments/<parentId>/replies/

…but never the single-comment read path. So after unhiding, /api/members/comments/:id/ kept serving the stale "hidden" payload (while the freshly-purged browse path rendered the comment right below the error). The reverse direction is worse: after hiding a comment, the cached read endpoint kept serving its content until TTL.

These purge paths predate the permalink feature (added Dec 2025 in 3df0e8c), which introduced the dependency on the read endpoint — closest precedent is a489d5a, which added the /replies/ path for the same reason.

Solution

  • setCacheInvalidationHeaders now also emits /api/members/comments/<id>/ (appended last, so existing unanchored matchers stay valid)
  • Replaced the six duplicated inline copies of the purge-path logic (member edit, add, like, unlike, dislike, undislike) with calls to the existing shared helper — net −9 lines
  • Updated header assertions, added a unit test for the top-level case, regenerated snapshots; sites where the header now contains a dynamically-created comment id use stringMatching so snapshots stay stable across runs

Note for review: this only requests the purge — worth confirming with the Pro team that the edge purge rules cover /api/members/comments/:id/ (they should if matching is prefix-based on /api/members/comments/).

Local verification: test/unit/server/services/comments (39 passing), test/e2e-api/members-comments (102 passing, twice — no snapshot drift), test/e2e-api/admin/comments.test.js (88 passing), test/e2e-api/admin/member-commenting.test.js (30 passing).

Replaces #28492 (same commit, recreated from an in-repo branch instead of a fork).

🤖 Generated with Claude Code

…a comment

no ref

- comment permalinks (the "View on post" action in admin and shared
  comment links) load the comment via GET /api/members/comments/:id/
  to decide whether to scroll to it or show an error notice
- on hosts that cache the members API at the edge (purged via the
  X-Cache-Invalidate header), comment mutations only purged the post
  browse and parent replies paths, so the single-comment read path
  kept serving a stale "hidden" payload after a comment was unhidden,
  and kept serving hidden content after a comment was hidden
- comment mutations now also purge /api/members/comments/:id/, and the
  six duplicated copies of the purge-path logic are consolidated into
  the existing shared setCacheInvalidationHeaders helper

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2c0580db-a609-407f-b839-bfea3d25d882

📥 Commits

Reviewing files that changed from the base of the PR and between d180686 and 29f10e7.

⛔ Files ignored due to path filters (2)
  • ghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • ghost/core/core/server/services/comments/comments-controller.js
  • ghost/core/test/e2e-api/admin/comments.test.js
  • ghost/core/test/e2e-api/members-comments/comments.test.js
  • ghost/core/test/unit/server/services/comments/comments-controller.test.js

Walkthrough

This PR refactors cache-invalidation header construction in the comments controller from duplicated per-method logic to a centralized setCacheInvalidationHeaders() helper. The invalidation paths are expanded to include the specific mutated comment endpoint (/api/members/comments/${model.id}/) alongside existing post and reply paths. All comment mutation endpoints (edit, add, like, unlike, dislike, undislike) are updated to call the helper. Unit tests are updated with explicit comment IDs in fixtures and expanded header expectations, including a new test for top-level comment invalidation. Admin and member E2E tests are updated to assert the broader cache-invalidation behavior across hide, pin, like, dislike, and edit operations.

Possibly related PRs

  • TryGhost/Ghost#28020: Modifies dislike/undislike flow and X-Cache-Invalidate header logic in the same comments controller.
  • TryGhost/Ghost#28297: Changes how cache-invalidation paths are computed in comments controller mutation flows and updated in test assertions.

Suggested labels

community

Suggested reviewers

  • kevinansfield
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary bug fix: addressing the 'linked comment is no longer available' error after unhiding comments, which directly matches the changeset's core objective.
Description check ✅ Passed The description thoroughly explains the problem, root cause (missing single-comment cache purge path), and the solution implemented in the changeset, with verification details.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-comment-read-cache-invalidation

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.43.0)
ghost/core/test/e2e-api/admin/comments.test.js
ghost/core/test/e2e-api/members-comments/comments.test.js

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@luissazevedo luissazevedo marked this pull request as ready for review June 11, 2026 09:10
@jonatansberg jonatansberg merged commit b9b2f65 into main Jun 11, 2026
71 of 88 checks passed
@jonatansberg jonatansberg deleted the fix-comment-read-cache-invalidation branch June 11, 2026 12:18
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.

2 participants