Skip to content
曾亮懿 ZengLiangYi Blog

Parsing Cursor SQLite: Debugging Missing Messages Across Tables

Check paired databases, key prefixes, unknown versions, and orphan discovery to distinguish unreadable data, parsing omissions, and absent records.

曾亮懿

曾亮懿

4 min read
Table of contents Show Hide

When imported messages are missing, identify the layer that did not produce expected data: file reading, table queries, conversation key matching, or field extraction. An empty result does not directly establish that the source tool deleted records, and a version warning cannot automatically be ignored.

This article follows ChatCrystal’s Cursor adapter at 7642768. See the Cursor import guide for installation, directories, and full import steps. Here the focus is parser diagnosis and coverage.

Establish that files belong to the same user data

Workspace databases supply Composer metadata; the global database supplies bubble messages. The adapter also obtains project directories from workspace.json. Copying only one store, or mixing in another machine’s global database, can leave conversation IDs without matching messages.

The database reader loads the main file into a sql.js memory instance, retries after 500 milliseconds on failure, and then returns null if it fails again. This is not precise classification of locks, permissions, or corruption, and it does not replay WAL.

SQLite’s WAL reference explains that committed changes can remain in the log. Investigating a live database requires a consistent view; consult the official backup mechanism. A main-file-only read is not evidence of every latest record. Do not experiment by deleting files from the original database.

Query the right table and key

Inspect tables in a test copy, then query keys recognized by the adapter. The following session ID is constructed:

-- Workspace database
SELECT value FROM ItemTable
WHERE [key] = 'composer.composerData';

-- Global database
SELECT [key], value FROM cursorDiskKV
WHERE [key] LIKE 'bubbleId:sample-session:%';

A missing cursorDiskKV table differs from an existing table with no matching rows. Keys have the shape bubbleId:<composerId>:<bubbleId>. A prefix matches a whole conversation, while an exact known key can use equality. This storage does not require LIKE for every query.

For a new implementation, bind parameters and handle the wildcard meaning of % and _ separately. The current adapter interpolates strings; this recommendation is not an already implemented protection. Ordinary UUIDs lack these wildcards, but that does not make every abnormal ID safe to handle identically.

Version and role checks are not full schema validation

For _v > 3, the code warns and continues reading known fields. This does not establish full compatibility with a new structure. Construct samples with _v: 4, renamed fields, and incorrect text types; inspect retained content and whether failures can be diagnosed.

The current code treats type === 1 as user and everything else as assistant, skipping assistant bubbles without body text. This is an implementation rule, not correct classification of every unknown type. An empty body alone also does not prove a streaming intermediate.

Thinking retention has prerequisites

When allThinkingBlocks is a nonempty array, the code collects each block’s thinking or text; otherwise it can fall back to top-level thinking. However, assistant bubbles without bodies are skipped before thinking extraction. An assistant record containing only reasoning can therefore be absent from imported messages.

Test body plus reasoning, top-level reasoning only, and a nonempty block array without usable text separately. Format support describes these known paths; it is not complete preservation of every reasoning record.

Orphan checks sample candidates

Global discovery extracts Composer IDs from keys, excludes known workspace conversations, and checks at most 20 candidate bubbles for nonempty text. The bubbleId: prefix has nine characters, so SQL SUBSTR([key], 10) begins at the ID.

This check has no ordering clause and does not inspect every bubble in the conversation. Placing usable text beyond the first 20 rows is an important failure sample. The LIMIT optimization cannot be described as lossless coverage. Only after discovery does parsing read every matching row.

Compare changed records, not only totals

Workspace paths are stripped of their prefix and URL-decoded for project display. The current conversion turns file:///tmp/project into tmp/project, losing the POSIX root slash; it does not guarantee a usable absolute path. Invalid encoding can cause a workspace to be skipped; a directory hash is not a reliable project name. Also inspect incremental import metadata: a known Composer’s creation time can serve as fileMtime, and combining that with global file size cannot establish detection of every content change.

Prepare a conversation with known messages, modify content in a test copy, import again, and compare actual message updates. Record files, tables, keys, fields, versions, and imported IDs to turn a missing result into a specific diagnosis. After parsing checks, inspect whether generated notes retain critical conditions. See Claude Code JSONL filtering for related event-filtering methods.

Share:

Related Posts

Importing Cursor history: workspace metadata and global messages

Trace how ChatCrystal joins Cursor SQLite data, discovers orphan sessions, and extracts messages, then verify the import with a known conversation.

曾亮懿 曾亮懿
4 min read
#ChatCrystal #Cursor #SQLite #Data parsing

AI Conversation Formats: JSONL Events vs SQLite Snapshots

Compare 5 AI adapter formats: separate JSONL events from SQLite keys, and inspect indexed, orphan, and unknown-version chat transcripts in memory stores.

曾亮懿 曾亮懿
4 min read
#ChatCrystal #Node.js #SQLite

How Claude Code, Cursor, Copilot, and Trae Store Chats on Disk

Compare Claude Code, Cursor, Copilot, and Trae storage paths. Distinguish raw copies from exports, and verify migration retention with test manifests.

曾亮懿 曾亮懿
4 min read
#ChatCrystal #Node.js #SQLite

From writing to code

Explore ChatCrystal, developer tools and daily learning notes on my GitHub. Questions about the implementation are welcome.