Skip to content

fix(memory): preserve existing DB when sqlite3_open fails - #63

Merged
adriannoes merged 3 commits into
developmentfrom
cursor/critical-bug-management-f181
Sep 13, 2026
Merged

adriannoes merged 3 commits into
developmentfrom
cursor/critical-bug-management-f181

Conversation

@cursor

@cursor cursor Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Summary

  • memory_init no longer remove()s an existing SQLite file when sqlite3_open fails. Permissions or transient I/O no longer destroy sessions, long-term memories, or cron jobs.
  • Reimplemented on current development (includes fix(providers): fail closed when Anthropic parse realloc fails聽#80). Not a cherry-pick of the original Bot commit.
  • Isolated to src/core/memory.c and tests/test_memory.c. Missing-file open failures still unlink and retry.

Test plan

  • test_existing_db_preserved_on_open_failure (red: chmod 000 open-fail path deleted and recreated the DB; green: init returns -1 and data is intact)
  • make test_memory
  • CI=true make test
  • make static

Refs: #63


Note

Medium Risk
Changes startup behavior for the persistent memory store; failures now fail closed without wiping data, which is safer but leaves the agent unable to start until the DB is readable again.

Overview
memory_init no longer deletes an existing SQLite file when sqlite3_open fails (e.g. permissions or transient I/O). It logs the error, closes the handle, and returns -1 instead of remove() + recreate. The unlink-and-retry path is unchanged when the DB path did not exist before open.

A new unit test (test_existing_db_preserved_on_open_failure) chmods the DB to block opens, asserts init fails without data loss, then restores permissions and verifies recall still works. CHANGELOG documents the fix.

Reviewed by Cursor Bugbot for commit 5f8d420. Bugbot is set up for automated code reviews on this repo. Configure here.

Skip remove() when sqlite3_open fails on a path that already exists.
Deleting it caused silent data loss on permissions or transient I/O.

Refs: #63
@adriannoes
adriannoes force-pushed the cursor/critical-bug-management-f181 branch from ac3c72e to c6717f3 Compare September 13, 2026 07:10
@adriannoes
adriannoes changed the base branch from main to development September 13, 2026 07:11
@adriannoes
adriannoes marked this pull request as ready for review September 13, 2026 07:11
@adriannoes
adriannoes self-requested a review as a code owner September 13, 2026 07:11

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Could not approve: GitHub rejects a self-approval because this bot authored the PR. Cursor Bugbot and Cursor Security Agent both passed with no findings that need human review. adriannoes is already assigned; no additional reviewers were added.

Open in Web聽View Automation聽

Sent by Cursor Approval Agent: Pull Request Router and Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

ShellClaw is a C99 edge agent (Jetson / Raspberry Pi) with sessions, long-term memories, and cron in one SQLite file. This PR closes the remaining data-loss hole in memory_init: commit 4268d69 already refused to delete an existing DB on schema failure (CR-18), but sqlite3_open failure still called remove() and retried. Five lines in src/core/memory.c now return -1 when path_exists was true, matching that schema-mismatch path. init_subsystems already aborts startup on that -1. I ran test_memory as uid 1000; all tests passed, including the new case.

Must Fix

None. Production behavior matches the PR claim. Missing-path open failures still unlink and retry. No injection, secrets, or new coupling.

Should Fix

  1. src/core/memory.c (new fprintf on the open-fail path): capture sqlite3_errmsg(g_db) before sqlite3_close. After close the message is gone. Operators on EACCES or I/O currently get only the path, then bootstrap's generic memory init failed.
  2. tests/test_memory.c test_existing_db_preserved_on_open_failure: chmod(path, 0000) does not block sqlite3_open as root. sudo ./build/test_memory fails at ASSERT(memory_init(path) == -1). GitHub ubuntu-24.04 is non-root, so required CI should pass. Skip when geteuid()==0, or occupy the path with a directory (sqlite3_open fails even as root; the old remove() of an empty dir would still recreate). Restore 0600 if an ASSERT fires after chmod; a failed root run left a mode-000 file in /tmp.

Nice to Have

  • Document fail-closed existing files in memory.h (memory_init still only says it creates the file if missing).
  • A SHELLCLAW_TEST open-fail hook would be root-proof, same idea as the Anthropic realloc hook in #80.
  • test_corrupted_db_recreated still has a name that implies recreate. Pre-existing; garbage files usually fail in run_schema, not sqlite3_open.

Positive Highlights

  • Same file_existed gate as the schema path. No new control-flow style.
  • The regression test would have been red on the old unlink-and-recreate: the owner can unlink a mode-000 file they own, then sqlite3_open creates an empty DB and init returns 0 with data gone.
  • Isolated to memory.c + test_memory.c, CHANGELOG Unreleased Fixed line in a separate docs commit, Conventional Commits, no secrets.
Open in Web聽View Automation聽

Sent by Cursor Automation: Adrianno鈥檚 personal code review

Comment thread src/core/memory.c Outdated
Comment thread tests/test_memory.c Outdated
@adriannoes adriannoes self-assigned this Sep 13, 2026
Capture sqlite3_errmsg before close. Skip the chmod-000 case when
sqlite3_open still succeeds (root/DAC), and restore mode 0600 if the
init-fail assertion fires.

Refs: #63

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could not approve: GitHub rejects a self-approval because this bot authored the PR. Cursor Bugbot and Cursor Security Agent both passed with no findings that need human review. adriannoes is already assigned; no additional reviewers were added.

Open in Web聽View Automation聽

Sent by Cursor Approval Agent: Pull Request Router and Approver

@adriannoes
adriannoes merged commit 50d0bfb into development Sep 13, 2026
4 checks passed
@adriannoes
adriannoes deleted the cursor/critical-bug-management-f181 branch September 13, 2026 07:43
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