{"record":{"id":"04a5013f611a5da6","repo":"gastownhall/beads","slug":"get-events-since-v-w","errorCode":null,"errorMessage":"get events since %v: %w","messagePattern":"get events since (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/events.go","lineNumber":53,"sourceCode":"\n\treturn scanEvents(rows)\n}\n\n// GetAllEventsSinceInTx returns all events created after the given time,\n// querying both events and wisp_events tables.\nfunc GetAllEventsSinceInTx(ctx context.Context, tx *sql.Tx, since time.Time) ([]*types.Event, error) {\n\trows, err := tx.QueryContext(ctx, `\n\t\tSELECT id, issue_id, event_type, actor, old_value, new_value, comment, created_at\n\t\tFROM events\n\t\tWHERE created_at > ?\n\t\tUNION ALL\n\t\tSELECT id, issue_id, event_type, actor, old_value, new_value, comment, created_at\n\t\tFROM wisp_events\n\t\tWHERE created_at > ?\n\t\tORDER BY created_at ASC\n\t`, since, since)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get events since %v: %w\", since, err)\n\t}\n\tdefer rows.Close()\n\n\treturn scanEvents(rows)\n}\n\n// Event keyset-read bounds. The API's change feed pages the durable events\n// table with a bounded limit (default when the caller passes <= 0, hard cap\n// otherwise).\nconst (\n\tdefaultEventsSinceLimit = 100\n\tmaxEventsSinceLimit     = 500\n)\n\n// EventsSinceInTx returns durable events strictly after the (createdAt, id)\n// keyset cursor, ordered by (created_at ASC, id ASC) and bounded by limit.\n// issueID != \"\" scopes the feed to one bead's history; \"\" returns all issues'.\n//","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/events.go#L35-L71","documentation":"GetAllEventsSinceInTx selects all wisp events with created_at after the given timestamp, ordered ascending. A query failure at execution time is wrapped with the `since` value included. It means incremental event sync cannot proceed because the wisp_events table could not be read from that point.","triggerScenarios":"Calling GetAllEventsSinceInTx when the `SELECT ... FROM wisp_events WHERE created_at > ?` query fails — the wisp_events table not existing yet (pre-migration database), a connection error, or an unparseable/invalid since parameter.","commonSituations":"Running sync against an old database before wisp tables were created; clock/timestamp formats from other tooling causing driver conversion errors; remote Dolt server unreachable.","solutions":["If the wrapped cause is 'no such table: wisp_events', run bd's migrations so wisp tables exist.","Retry on transient connection errors.","Ensure `since` is a valid timestamp the driver can bind (time.Time or correct string format).","Check connectivity to the database and server logs."],"exampleFix":"// before\nclient.GetAllEventsSince(ctx, \"2026-08-30 not-a-time\")\n// after\nclient.GetAllEventsSince(ctx, time.Now().Add(-24*time.Hour))","handlingStrategy":"try-catch","validationCode":"var hasWisps int\n_ = db.QueryRow(\"SELECT COUNT(*) FROM information_schema.tables WHERE table_name='wisp_events'\").Scan(&hasWisps)\nif hasWisps == 0 { return errors.New(\"wisp_events missing; run migrations first\") }\nif since.IsZero() { return errors.New(\"since must be a valid timestamp\") }","typeGuard":"func isEventsSinceErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"get events since \")\n}","tryCatchPattern":"events, err := GetAllEventsSinceInTx(ctx, tx, since)\nif err != nil {\n    if isEventsSinceErr(err) && strings.Contains(err.Error(), \"no such table\") {\n        return migrateThenResync(ctx, since)\n    }\n    return fmt.Errorf(\"incremental event sync failed: %w\", err)\n}","preventionTips":["Ensure wisp migrations are applied before incremental sync.","Always pass a valid time.Time for `since`.","Persist a high-water mark timestamp for resumable sync.","Retry transient connection failures with backoff."],"tags":["go","sql","storage","events","sync"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}