Thinkmill / Thinkmill/keystatic

saving body-only edit deletes index.json without re-adding it (entryLayout: 'content' + format: {data:'json'})

Open
#1,617 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2.4k
Forks
159
Avg merge
21h 41m
Merged PRs (30d)
2

Description

Description

When saving an entry that uses entryLayout: 'content' with format: { data: 'json' }
(metadata in index.json, body in a separate content.mdoc/markdown file), editing
only the body content sometimes causes Keystatic to emit a Git commit that
deletes index.json without re-adding it, even though the metadata itself was
unchanged. Content.mdoc updates correctly; index.json is just gone.

Environment
  • @keystatic/core: 0.6.9
  • @keystatic/astro: 6.0.0
  • Storage: kind: 'github'
  • Collection: path: 'src/content/blog/*/', entryLayout: 'content', format: { data: 'json' }
Reproduction
  1. Open an existing entry in the GitHub-storage-mode admin UI.
  2. Edit only the markdoc body field (leave title/metadata untouched).
  3. Click Save.
  4. Inspect the resulting commit: index.json is deleted, content.mdoc is
    modified in place — no addition recreates index.json.
Evidence

Commit example (repo private, redacted path):
content.mdoc | 2 ++
index.json | 24 ----------------------
2 files changed, 2 insertions(+), 24 deletions(-)
delete mode 100644 .../index.json

A follow-up save then fails with:
[GraphQL] A path was requested for deletion which does not exist as of commit oid ''
because index.json was already deleted by the prior save.

Suspected root cause

In useUpsertItem (keystatic-core-ui.js), filesToDelete is derived from
args.initialFiles (a snapshot captured when the entry form was loaded) and
additions is filtered by comparing blob sha against unscopedTree
(a client-side cached tree). If that cached tree is stale relative to the
actual GitHub HEAD (e.g. after a previous save, or a tab left open), an
unchanged file can end up filtered out of additions while still remaining
in filesToDelete, producing a commit with a deletion but no matching
re-addition.

Related

Same symptom as #1269, which was closed without a root cause. This report
includes a concrete reproducible commit diff.

Impact

Data loss — entries silently disappear from the CMS (Entry not found,
0 entries) until manually restored from git history.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Inspect useUpsertItem in keystatic-core-ui.js, focusing on how args.initialFiles, additions, filesToDelete, and the cached unscopedTree interact during a body-only save. Reproduce the GitHub-storage edit from the issue and verify that saving unchanged metadata retains index.json while updating content.mdoc, including on a follow-up save.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
content
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.