{"record":{"id":"8ce1bbec275dc31f","repo":"gastownhall/beads","slug":"clear-repo-mtime-w","errorCode":null,"errorMessage":"clear repo mtime: %w","messagePattern":"clear repo mtime: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/bulk_ops.go","lineNumber":447,"sourceCode":"\t\tVALUES (?, ?, ?, NOW())\n\t\tON DUPLICATE KEY UPDATE\n\t\t\tjsonl_path = VALUES(jsonl_path),\n\t\t\tmtime_ns = VALUES(mtime_ns),\n\t\t\tlast_checked = NOW()\n\t`, repoPath, jsonlPath, mtimeNs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"set repo mtime: %w\", err)\n\t}\n\treturn nil\n}\n\n// ClearRepoMtimeInTx removes the mtime cache entry for a repo path.\nfunc ClearRepoMtimeInTx(ctx context.Context, tx *sql.Tx, repoPath string) error {\n\t// Expand ~ and resolve to absolute path to match stored format.\n\tabsPath := expandAndAbsPath(repoPath)\n\t_, err := tx.ExecContext(ctx, `DELETE FROM repo_mtimes WHERE repo_path = ?`, absPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"clear repo mtime: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc expandAndAbsPath(p string) string {\n\tif strings.HasPrefix(p, \"~\") {\n\t\thome, err := os.UserHomeDir()\n\t\tif err == nil {\n\t\t\tif p == \"~\" {\n\t\t\t\tp = home\n\t\t\t} else {\n\t\t\t\tp = filepath.Join(home, p[2:])\n\t\t\t}\n\t\t}\n\t}\n\tabs, err := filepath.Abs(p)\n\tif err != nil {\n\t\treturn p","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/bulk_ops.go#L429-L465","documentation":"ClearRepoMtimeInTx deletes the repo_mtimes row matching the expanded, absolute repo path and wraps any SQL failure with \"clear repo mtime: %w\". It is used to invalidate the jsonl mtime cache so the next sync re-reads the file. The error indicates the DELETE statement itself failed inside the transaction.","triggerScenarios":"Calling ClearRepoMtimeInTx when: the repo_mtimes table is missing (unmigrated schema), the transaction was aborted by a prior error, the context was cancelled, or the DB connection dropped before/during the DELETE.","commonSituations":"Stale-schema databases after upgrading bd; callers that ignore an earlier failed statement and keep using the aborted transaction; cancellation during an import that tries to clean up cache entries; network blips to a remote Dolt server.","solutions":["Apply pending schema migrations so repo_mtimes exists.","Roll back and retry the whole transaction; a DELETE can fail from a poisoned transaction.","Check the wrapped error for 'lock wait timeout' and look for competing bd processes holding locks.","Ensure the context passed in is not already cancelled/deadlined before the cleanup call."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if ctx.Err() != nil { return ctx.Err() }","typeGuard":null,"tryCatchPattern":"if err := issueops.ClearRepoMtimeInTx(ctx, tx, repoPath); err != nil {\n    tx.Rollback()\n    return fmt.Errorf(\"cache invalidation failed: %w\", err)\n}","preventionTips":["Roll back transactions immediately when any statement fails.","Run migrations after upgrading bd before running sync.","Check for concurrent bd processes before long cleanup transactions.","Always pass the same repoPath format (absolute) used when setting the entry."],"tags":["database","storage","sql","cache-invalidation"],"backgroundTag":"sql-exec-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}