From 0c016d3ee3632d10cff96877bd9edba0c60ebbe5 Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Tue, 27 Jan 2026 15:38:25 -0800 Subject: [PATCH] fix(core): correct cursor direction for descending pagination in list_agent_blocks_async (#9122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cursor-based pagination was not accounting for sort order. When using descending order (the default), "after cursor X" should return items with id < X (items that come after X in the descending result set), but the code was using id > X which caused infinite loops in clients iterating through pages. This fix adjusts the cursor comparison based on the sort order: - ascending: after=id > X, before=id < X - descending: after=id < X, before=id > X Note: Other pagination methods (list_agent_sources_async, list_agent_tools_async, list_agent_groups_async) may have the same issue and should be audited. 🐾 Generated with [Letta Code](https://letta.com) Co-authored-by: Letta --- letta/services/agent_manager.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index eaee6ec3..f7874475 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -3046,10 +3046,19 @@ class AgentManager: ) # Apply cursor-based pagination - if before: - query = query.where(BlockModel.id < before) - if after: - query = query.where(BlockModel.id > after) + # Note: cursor direction must account for sort order + # - ascending order: "after X" means id > X, "before X" means id < X + # - descending order: "after X" means id < X, "before X" means id > X + if ascending: + if before: + query = query.where(BlockModel.id < before) + if after: + query = query.where(BlockModel.id > after) + else: + if before: + query = query.where(BlockModel.id > before) + if after: + query = query.where(BlockModel.id < after) # Apply sorting - use id instead of created_at for core memory blocks if ascending: