Skip to content

fix(catalog): store unsigned 64-bit stat identities on Windows - #24

Closed
jason-murray wants to merge 1 commit into
xhluca:mainfrom
jason-murray:fix/catalog-unsigned-stat-ids
Closed

jason-murray wants to merge 1 commit into
xhluca:mainfrom
jason-murray:fix/catalog-unsigned-stat-ids

Conversation

@jason-murray

Copy link
Copy Markdown

Problem

On Windows, smigrate catalog refresh reports a root error for every root and indexes nothing:

roots: 2
files_seen: 2
scanned: 2
root_errors: 2
statuses: {}

catalog roots list shows each root as error with last_error = scan_failed.

Cause

Python 3.12+ on Windows reports st_dev (the volume serial number) and st_ino (the file ID) as unsigned 64-bit integers. On the affected machine, os.stat(...).st_dev for C: is 11573954572006337284, which is above SQLite's INTEGER maximum (2**63 - 1). Binding it in _upsert_session / _upsert_virtual_session raises:

OverflowError: Python int too large to convert to SQLite INTEGER

The broad except Exception in _refresh_root / _refresh_opencode_root records that as scan_failed, so it happens on every root on the volume (Claude and OpenCode here). Roughly half of volume serials are at or above 2**63, so plenty of Windows users will hit this. ReFS / Dev Drive file IDs can overflow st_ino the same way.

Fix

A _sqlite_int helper folds an unsigned 64-bit value into the signed INTEGER range (two's complement). It is applied:

  • where device/inode are bound in both upsert statements, and
  • in the unchanged-file comparison in _refresh_root, so incremental refresh still matches stored rows.

Values already in range are unchanged, so existing catalogs on Linux/macOS are unaffected. No schema change.

Testing

  • New regression test test_refresh_stores_unsigned_64_bit_stat_identities fakes device = 2**64 - 1 and inode = 2**63 + 7, then checks that the first refresh indexes the session with no root errors and the second reports it unchanged. It fails on main and passes with this change.
  • ruff check . and ruff format --check src tests scripts .github/scripts .github/tests pass. python -m unittest discover .github/tests passes.
  • Full pytest was run on Windows only (no Linux machine available). Three native test modules can't be imported there (termios), and many target-integration tests fail on Windows for unrelated reasons. I compared failing test IDs between main and this branch: the sets are identical (1181 each), with no new failures. I'm relying on CI for the Linux run.
  • Manual: with the same patch applied to an installed 0.12.0, catalog refresh on the affected machine indexes 108 files with root_errors: 0, and a second refresh reports 107 unchanged.

A possible follow-up (not in this PR): scan_failed hides the underlying exception type, which made this hard to diagnose. Recording the exception class name (no content) in last_error would have shown the cause right away.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WW649bqoSmoitkj96NB5WB

On Windows, Python reports st_dev (volume serial) and st_ino (file ID) as
unsigned 64-bit integers. When either exceeds 2**63 - 1, binding it to the
catalog's INTEGER columns raises OverflowError, which the refresh loop
records as scan_failed, so every root on that volume reports a root error
and nothing is indexed.

Fold both values into SQLite's signed INTEGER range before storing them
and before comparing against stored rows, so incremental refresh keeps
detecting unchanged files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WW649bqoSmoitkj96NB5WB
@github-actions

Copy link
Copy Markdown

🔴 Contributor eligibility requirements not met for @jason-murray.

  • 🟢 Account created: 2014-10-27; age: 4381 days (required: at least six calendar months).
  • 🔴 Contributions dated on or before 2026-09-25: 0 (required: at least 100).

Pull request authors must have a GitHub account at least six calendar months old and at least 100 GitHub contributions dated on or before the date one calendar month before the check.

See the contributor eligibility policy.

Closing this PR because these requirements were not met. This policy is intended to reduce spam from AI agents; it is not an assessment of the quality of your change.

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.

1 participant