Files
intotheeast-com/docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md
T
m038andClaude Opus 4.8 e10496afe7 docs(solutions): BUG-001 part 2 — deleteAll doesn't rebuild page-tree index
Document why deleteAll() alone left a freshly-posted entry 404-ing on its
edit-prefill API lookup (regular-pages index keyed on config->checksum, which
survives a create under cache.check.method:folder) and why the fix adds
Cache::invalidateCache(). Note the H1/M8 travelling:true coverage gap as a
tracked residual. Cross-link from bugs-and-fixes.md.

Code review F8 (learnings) + F5 (residual-gap tracking).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
2026-07-07 08:32:20 +02:00

4.3 KiB
Raw Blame History

title, date, category, module, problem_type, component, severity, symptoms, root_cause, resolution_type, related_components, tags
title date category module problem_type component severity symptoms root_cause resolution_type related_components tags
cache.deleteAll() doesn't rebuild the page-tree index — a freshly-posted entry 404s when opened for editing 2026-07-07 integration-issues cache-on-save integration_issue plugin high
A just-posted journal entry is written to disk but the API 404s on it (GET /api/v1/pages{route})
Opening the entry you just created for editing shows 'This entry no longer exists — it may have been deleted'
The entry DOES appear in the trip feed, but the edit prefill fetch can't find it until the next unrelated cache bump
Intermittent — only bites when the page-tree index survives the create
incomplete_setup code_fix
documentation
development_workflow
grav
cache
forms
page-tree

cache.deleteAll() doesn't rebuild the page-tree index

Context — this is BUG-001 Part 2

BUG-001 ("new entry not visible after form submission") was fixed by wiring $this->grav['cache']->deleteAll() into the cache-on-save plugin's onFormProcessed hook. That made new entries appear in the trip feed immediately. It was not the whole story: deleteAll() drops the Doctrine store (rendered-page cache, feed HTML, etc.) but does not force Grav to rebuild its regular-pages index.

The gap only surfaced once the shared /post form gained an edit mode (?edit=<route>), whose prefill does GET /api/v1/pages{route}. On a fresh create that request would 404 — so the owner opening the entry they had just posted saw "This entry no longer exists."

Root cause

Grav's regular-pages index is keyed on:

md5(dirs + folderHash + config->checksum() + lang)   // Pages::buildRegularPages

With cache.check.method: folder (our setting), the folderHash component does not necessarily change when a new child folder is added inside an existing tree — so the index key stays the same and the stale index (missing the new entry) is reused. deleteAll() clears cache stores but does not change any of the inputs to that key, so the tree is not rebuilt. The new page is on disk and in the feed (which re-reads children), but the API lookup by route resolves through the cached index and 404s.

Fix

Add a second invalidation step alongside deleteAll():

use Grav\Common\Cache;
// ...
$this->grav['cache']->deleteAll();
Cache::invalidateCache();   // touch(system.yaml) → bumps config->checksum()

Cache::invalidateCache() is lightweight and idempotent — it touch()es system.yaml, calls clearstatcache() and opcache_reset() (verified in Grav core Cache.php). Touching system.yaml bumps config->checksum(), which changes the index key, so the tree rebuilds on the next request and the new entry becomes resolvable by route.

Latch it — the hook fires 4× per submit

onFormProcessed fires once per process: action, and post-form.md has four (add_page, upload, message, reset). Without a guard the deleteAll() + invalidateCache() pair runs four times per post (a full store wipe + system.yaml touch each time). Gate it with a once-per-request latch ($cacheInvalidated), the same pattern already used for photo reconciliation ($photosReconciled). See user/plugins/cache-on-save/cache-on-save.php.

How to verify

  1. Post a new entry via /post.
  2. From the trip feed, click the new card's Edit link.
  3. The form prefills with the entry's title/body — no "no longer exists" banner.

Regression test: tests/ui/post/edit-mode.spec.js ES1 (create → open the feed card's Edit link → change title + body → Save → assert on disk).

Residual coverage gap (tracked, not fixed here)

tests/ui/home/home.spec.js H1 and tests/ui/maps/maps.spec.js M8 require site.travelling: true to exercise the active-trip home feed + home GPX map. The committed local site.yaml runs travelling: false (owner's testing config, intentionally not committed as true), so both specs skip loudly with a reason rather than fail misleadingly. They validate whenever the site is in travelling mode. This is a known gap in this environment, not a silent hole — provisioning travelling: true in a dedicated test config would close it.