{"record":{"id":"869e6edd4ae55fab","repo":"thedotmack/claude-mem","slug":"schema-v46-failed-to-copy-target-table-targe","errorCode":null,"errorMessage":"schema v46: failed to copy ${target.table}.${target.column} row ${mismatch.row_id} exactly","messagePattern":"schema v46: failed to copy (.+?)\\.(.+?) row (.+?) exactly","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/services/sqlite/SessionStore.ts","lineNumber":754,"sourceCode":"        if (isText(source)) continue;\n        if (columns.some(info => info.name === target.temporary)) {\n          throw new Error(`schema v46: unexpected temporary column ${target.table}.${target.temporary}`);\n        }\n\n        this.db.run(\n          `ALTER TABLE ${target.table} ADD COLUMN ${target.temporary} TEXT NOT NULL DEFAULT '1'`\n        );\n        this.db.run(\n          `UPDATE ${target.table} SET ${target.temporary} = CAST(${target.column} AS TEXT)`\n        );\n        const mismatch = this.db.prepare(`\n          SELECT CAST(id AS TEXT) AS row_id\n          FROM ${target.table}\n          WHERE ${target.temporary} <> CAST(${target.column} AS TEXT)\n          LIMIT 1\n        `).get() as { row_id: string } | undefined;\n        if (mismatch) {\n          throw new Error(\n            `schema v46: failed to copy ${target.table}.${target.column} row ${mismatch.row_id} exactly`\n          );\n        }\n        this.db.run(`ALTER TABLE ${target.table} DROP COLUMN ${target.column}`);\n        this.db.run(\n          `ALTER TABLE ${target.table} RENAME COLUMN ${target.temporary} TO ${target.column}`\n        );\n      }\n\n      this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)')\n        .run(46, new Date().toISOString());\n    });\n    tx();\n  }\n\n  /**\n   * Canonical-v2 entity heads and durable tombstone queue. Revisions remain\n   * decimal TEXT so a remote value is never rounded through a JS number.","sourceCodeStart":736,"sourceCodeEnd":772,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sqlite/SessionStore.ts#L736-L772","documentation":"After copying revision values into the temporary TEXT column, the migration verifies at least one row matches exactly (temporary <> CAST(column AS TEXT) finds no row). A mismatch means the copy did not preserve the value byte-for-byte, so it refuses to drop/rename columns, preventing silent data loss.","triggerScenarios":"The post-copy verification SELECT in ensureSyncRevisionTextAffinity returns a row where the temporary column differs from CAST(original AS TEXT).","commonSituations":"Concurrent writers modifying rows mid-migration; SQLite collation/type affinity quirks altering copied values; an external process touching the DB during startup.","solutions":["Ensure no other process is writing to the database and retry startup","Restore a pre-migration backup","Compare the flagged row's two values to diagnose the affinity behavior","Report as a bug if it reproduces with exclusive access"],"exampleFix":"-- identify the mismatching row\nSELECT CAST(id AS TEXT) FROM sdk_sessions WHERE __tmp_sync_rev <> CAST(sync_rev AS TEXT) LIMIT 1;","handlingStrategy":"retry","validationCode":"// ensure exclusive access before startup\nconst lock = db.query('PRAGMA database_list').all();\n// also verify no other worker holds a write lock:\nconst busy = db.query('PRAGMA busy_timeout').get();","typeGuard":null,"tryCatchPattern":"try { store = new SessionStore(dbPath); } catch (e) {\n  if (String(e.message).includes('failed to copy') && String(e.message).startsWith('schema v46')) {\n    await retryWithExclusiveLock(() => new SessionStore(dbPath));\n  } else throw e;\n}","preventionTips":["Ensure only one process opens the DB during startup/migration","Stop background workers before upgrading","Keep a pre-migration backup for rollback"],"tags":["schema","migration","sqlite","data-integrity"],"backgroundTag":"schema-validation-failed","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}