Skip to content

qmd update keeps the old content of a file that became empty #991

Closed

Description

@ParkerRex

Impact

When an indexed markdown file is emptied (truncated to zero bytes or to whitespace only), qmd update skips it silently, counting it under no bucket, and its previous body stays active in the index. search, query and get keep returning content that no longer exists on disk. A file that is deleted is deactivated correctly; a file that is emptied is not.

Repro (main @ 04e4dbd)

export QMD_CONFIG_DIR=/tmp/qmd-cfg INDEX_PATH=/tmp/qmd-index.sqlite
mkdir -p /tmp/notes && printf '# A\n\nzebra body\n' > /tmp/notes/a.md
qmd collection add /tmp/notes --name notes      # Indexed: 1 new
: > /tmp/notes/a.md                             # file is now empty
qmd update                                      # Indexed: 0 new, 0 updated, 0 unchanged, 0 removed
qmd search zebra                                # still returns "zebra body"
qmd get qmd://notes/a.md                        # still returns the old body

Cause

In reindexCollection (src/store.ts) the path is added to seenPaths before the empty-content check. The empty check then continues, so the existing row is never updated and the end-of-pass deactivation skips it because the path counts as seen.

Suggested fix

Treat an emptied file the way a deleted file is treated: do not count it as seen, so the deactivation pass at the end marks the row inactive. Writing content back reactivates it through the normal insert path. Empty files at first index are already skipped, so this makes the two cases consistent.

I have a small fix with a test ready and will open a PR.

Activity

  1. added a commit that references this issue on Sep 26, 2026
    9cc0c2e
  2. yakold commented on Nov 14, 2026

    @yakold

    Confirmed on the latest published release (2.8.3, released 2026-08-16): the empty-file behaviour described here still reproduces, so the fix that is now on main is not shipped yet.

    Repro on 2.8.3 (facd35e):

    printf '#decision use Redis
    ' > ~/mem/MEMORY.md
    qmd collection add ~/mem --name mem        # indexed 1
    : > ~/mem/MEMORY.md                        # file emptied to 0 bytes
    qmd update                                # Indexed: 0 new, 0 updated, 1 unchanged, 0 removed
    qmd ls mem                                # still lists MEMORY.md at the old byte size
    qmd search "Redis" -c mem               # still returns the removed body
    qmd get qmd://mem/MEMORY.md               # still returns it
    qmd cleanup                               # reclaims orphan vectors only; the doc stays active
    rm ~/mem/MEMORY.md && qmd update          # "1 removed" - only removing the file deactivates it

    On main, src/store.ts now has retireIndexedFile handling a path that "became empty or grew past REINDEX_MAX_FILE_SIZE", so the fix exists upstream. #992 was closed without merge (merged_at: null), so the change appears to have landed via a commit rather than that PR. Either way, anyone on the latest npm release is still affected.

    Why it matters in practice: memory/agent tooling rewrites files in place and can legitimately truncate one to zero entries. When that happens the old body stays active and keeps being returned by search, query and get - deleted facts remain retrievable, which is a correctness hazard, not just stale metadata.

    Suggestion: cut a release that includes retireIndexedFile, or at minimum document the requirement, since 2.8.3 is the only version most installs resolve to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions