List Chats
System Intent
- What this is: Fetches a paginated list of the authenticated user's past AI chat conversations (most-recently-updated first) and surfaces them in the side menu.
Mermaid Diagram
flowchart TD
SideMenu["SideMenu (index.tsx)"] -->|useChatsFeed limit=12| useChatsFeed
useChatsFeed -->|fetchChats params cursor string| fetchChats
fetchChats -->|ZodSchema validate| FetchChatsParamsSchema
fetchChats -->|POST /chats/list| ChatsListLambda["chats-list Lambda"]
ChatsListLambda -->|Cognito JWT auth| CognitoAuthorizer
ChatsListLambda -->|DynamoDB query user+updatedAt| DynamoDB["DynamoDB Chats table"]
ChatsListLambda -->|conversations + next_cursor| fetchChats
fetchChats -->|FetchChatsResponse| useChatsFeed
useChatsFeed -->|getNextPageParam String coerce| useChatsFeed
useChatsFeed -->|pages| SideMenu
Flows
Flow: listChats
- Test files:
main/app/__tests__/chats-list-api.test.ts, main/app/__tests__/use-chats-api.test.ts - Core files:
main/app/lib/api/chats/useChatsApi.ts, main/app/lib/api/chats/fetchChats.ts, main/server/api/chats/list/app.py
Types
FetchChatsParams {
chat_id: string | null (optional)
cursor: string | null (optional — DynamoDB LastEvaluatedKey, must be string)
limit: number (optional, default 20, max 50)
}
ChatSummary {
id: string
title: string
last_message: string (relative date label: "Today", "Yesterday", weekday, or "Mon D")
updated_at: string (ISO timestamp)
}
FetchChatsResponse {
conversations: ChatSummary[]
next_cursor: string | null
}
Paths
| path | input | output | path-type | notes |
listChats.success | FetchChatsParams | FetchChatsResponse | happy path | Conversations ordered by updatedAt desc |
listChats.paginated | FetchChatsParams cursor=<string> | FetchChatsResponse | happy path | cursor coerced to string before Zod validation |
listChats.empty | FetchChatsParams | FetchChatsResponse conversations=[] | happy path | No conversations for user |
listChats.forbidden | FetchChatsParams | FetchChatsResponse conversations=[] | error | 403 from Lambda returns empty list silently |
listChats.error | any | conversationsErrorMessage="Something went wrong. Please try again." | error | Raw error logged to console.error; UI shows static string |
Pseudocode
// useChatsFeed — cursor coercion (prevents Zod invalid_type errors)
queryFn({ pageParam }) {
rawCursor = pageParam ?? params.cursor ?? null
cursor = rawCursor != null ? String(rawCursor) : null
return fetchChats({ ...params, cursor })
}
getNextPageParam(lastPage) {
return lastPage.next_cursor != null ? String(lastPage.next_cursor) : undefined
}
// index.tsx — error display
if (isConversationsError) {
console.error("[conversations] failed to load:", conversationsError)
}
conversationsErrorMessage = isConversationsError
? "Something went wrong. Please try again."
: undefined
The cursor coercion guards against next_cursor arriving as a non-string (e.g. number) from the server. Without coercion, FetchChatsParamsSchema.parse throws a Zod invalid_type error whose .message is a JSON array string. See docs/bugs/2026-05-07-conversations-cursor-object-invalid-type.md.
Logs
| Source | Location |
| Conversations load error | console.error in main/app/app/index.tsx (prefixed [conversations] failed to load:) |
| Chats feed query | step: "chats_feed_query_requested" via useLogging |
| Chat messages query | step: "chat_messages_query_requested" via useLogging |
Deployment
- Mechanism:
SAM - Deploy command:
# Deploy chats-list Lambda via SAM from repo root
sam deploy
- Notes: Lambda requires
CHATS_TABLE_NAME (default Chats) and MESSAGES_TABLE_NAME (default Messages) env vars. API Gateway uses Cognito JWT authorizer.