{"record":{"id":"ab8679ababfaf391","repo":"MemPalace/mempalace","slug":"since-event-id-since-event-id-r-not-found","errorCode":null,"errorMessage":"since_event_id {since_event_id!r} not found","messagePattern":"since_event_id (.+?) not found","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/logstream.py","lineNumber":742,"sourceCode":"        ):\n            if value is not None:\n                where.append(f\"{column} = ?\")\n                params.append(value)\n        if to_agent is not None:\n            where.append(\"(to_agent = ? OR to_agent = '*')\")\n            params.append(to_agent)\n        if since_created_at is not None:\n            where.append(\"created_at >= ?\")\n            params.append(since_created_at)\n\n        with self._lock:\n            conn = self._conn()\n            if since_event_id is not None:\n                anchor = conn.execute(\n                    \"SELECT rowid FROM events WHERE id = ?\", (since_event_id,)\n                ).fetchone()\n                if anchor is None:\n                    raise ValueError(f\"since_event_id {since_event_id!r} not found\")\n                where.append(\"rowid > ?\")\n                params.append(anchor[\"rowid\"])\n\n            sql = \"SELECT rowid, * FROM events\"\n            if where:\n                sql += \" WHERE \" + \" AND \".join(where)\n            sql += \" ORDER BY rowid ASC LIMIT ?\"\n            params.append(limit)\n            rows = conn.execute(sql, params).fetchall()\n            events = [self._event_dict(row) for row in rows]\n            return self._attach_artifact_ids(conn, events)\n\n    def latest_event_id(self) -> Optional[str]:\n        \"\"\"Id of the newest event, or None on an empty log.\n\n        Live-tail consumers (the SSE stream) capture this at connect time\n        as their starting cursor so they receive only post-connect events.\n        \"\"\"","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/logstream.py#L724-L760","documentation":"list_events was called with since_event_id set to an id that does not exist in the events table. That parameter is an exclusive cursor anchor: the rowid of the named event becomes the boundary ('rowid > ?') for pagination, so an unknown id has an unresolvable position and the call refuses rather than silently returning everything or nothing.","triggerScenarios":"list_events(since_event_id='evt_typo') after a copy error; paginating against a different db_path than the one the earlier page came from; using an id returned by an older query after the database was recreated; passing an artifact id ('art_...') instead of an event id.","commonSituations":"Long-running pollers that cache an anchor id across palace resets or db migrations; multi-replica reads where local replication lags the anchor's origin; consumers mixing up ids from artifacts and events.","solutions":["Only use ids returned in the same database's earlier response (each event dict's 'id' field).","After a reset or when unsure, re-baseline: fetch the newest events without since_event_id and re-anchor from the last row.","Cross-check with ls.get_event(since_event_id) before paginating; treat not-found as 'restart from head'."],"exampleFix":"// before\npage = ls.list_events(since_event_id=anchor_id)  # anchor from a pre-reset db\n// after\nif ls.get_event(anchor_id) is None:\n    anchor_id = None  # re-baseline from head\npage = ls.list_events(since_event_id=anchor_id, limit=50)\nif page:\n    anchor_id = page[-1][\"id\"]","handlingStrategy":"fallback","validationCode":"def valid_anchor(ls, event_id) -> bool:\n    try:\n        return ls.get_event(event_id) is not None\n    except ValueError:\n        return False\n\nif not valid_anchor(ls, anchor_id):\n    anchor_id = None  # re-baseline: read from head","typeGuard":null,"tryCatchPattern":"try:\n    page = ls.list_events(since_event_id=anchor_id, limit=50)\nexcept ValueError as e:\n    if \"not found\" in str(e):\n        anchor_id = None  # stale anchor after reset/migration: start over from head\n        page = ls.list_events(limit=50)\n    else:\n        raise","preventionTips":["Persist pagination cursors alongside the db identity (path + creation time) and invalidate on mismatch.","After any db recreate or migration, drop cached anchors and re-baseline from head.","Never feed artifact ids ('art_...') into since_event_id — it only accepts event ids."],"tags":["logstream","pagination","not-found","cursor"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}