- Updated python_coding_microproject.csv to use NVIDIA NIM Kimi K2 - Updated kali_linux_shell_simulator.csv to use NVIDIA NIM Kimi K2 - Made more general-purpose (flexible targets, expanded tools) - Added nemotron-mini-agent.csv for fast local inference via Ollama - Added nemotron-agent.csv for advanced reasoning via Ollama - Added wiki page: Projects for NVIDIA NIMs and Nemotron
7.8 KiB
Conversation API 401 Handling - Fix Complete ✅
Problem Solved
When creating a conversation or performing any conversation operation with an expired token, users would see errors in console instead of being redirected to login.
Original Bug:
POST http://localhost:3002/api/v1/conversations?agent_id=... 401 (Unauthorized)
❌ Failed to create conversation: 401 {"error":{"message":"Invalid or expired token"...}}
No redirect happened - user was left in broken state.
Solution Implemented: Phase 1 (Quick Fix)
Added fetchWithAuth Helper Function
File: apps/tenant-app/src/app/chat/page.tsx (lines 98-115)
/**
* Wrapper for fetch that handles 401 responses by triggering logout
* TODO: Migrate to centralized API service layer (conversations.ts)
*/
async function fetchWithAuth(url: string, options: RequestInit = {}): Promise<Response> {
const response = await fetch(url, options);
// Handle 401 - session expired
if (response.status === 401) {
console.warn('Chat API: 401 detected, triggering logout');
if (typeof window !== 'undefined') {
const { useAuthStore } = await import('@/stores/auth-store');
useAuthStore.getState().logout('expired');
}
}
return response;
}
All 8 Conversation Operations Fixed
| # | Function | Line | Endpoint | Method | Status |
|---|---|---|---|---|---|
| 1 | fetchConversationFiles |
223 | /conversations/{id}/files |
GET | ✅ Fixed |
| 2 | File deletion | 292 | /conversations/{id}/files/{fileId} |
DELETE | ✅ Fixed |
| 3 | createNewConversation |
779 | /conversations?agent_id=... |
POST | ✅ Fixed (YOUR BUG) |
| 4 | fetchLatestConversationId |
813 | /conversations?limit=1 |
GET | ✅ Fixed |
| 5 | saveMessageToConversation |
865 | /conversations/{id}/messages |
POST | ✅ Fixed |
| 6 | refreshConversationTitle |
890 | /conversations/{id} |
GET | ✅ Fixed |
| 7 | updateConversationName |
923 | /conversations/{id}?title=... |
PUT | ✅ Fixed |
| 8 | loadConversation (messages) |
950 | /conversations/{id}/messages |
GET | ✅ Fixed |
| 9 | loadConversation (details) |
988 | /conversations/{id} |
GET | ✅ Fixed |
All replaced:
// Before:
const response = await fetch(url, options);
// After:
const response = await fetchWithAuth(url, options);
Testing
Test Case 1: Create Conversation with Expired Token
- Login at http://localhost:3002
- Go to /chat
- Open DevTools Console:
localStorage.setItem('gt2_token', 'expired_token'); - Send first message (triggers conversation creation)
- Expected:
- ✅ Console: "Chat API: 401 detected, triggering logout"
- ✅ Redirect to
/login?session_expired=true - ✅ Red banner: "Your session has expired. Please log in again."
- ❌ NO error "Failed to create conversation: 401..."
Test Case 2: Load Conversation with Expired Token
- Login and create a conversation
- Note the conversation ID in URL:
/chat?conversation={id} - Corrupt token:
localStorage.setItem('gt2_token', 'invalid'); - Refresh page or click on conversation in sidebar
- Expected:
- ✅ Immediate redirect to login
- ✅ Session expired banner
- ❌ NO "Failed to load conversation messages"
Test Case 3: Save Message with Expired Token
- Have an active conversation
- Mid-chat, corrupt token:
localStorage.setItem('gt2_token', 'expired'); - Send another message
- Expected:
- ✅ Redirect to login (may happen during conversation creation or message save)
- ❌ NO error in chat
Test Case 4: Update Conversation Title with Expired Token
- Open a conversation
- Corrupt token:
localStorage.setItem('gt2_token', 'invalid'); - Click on title and try to rename conversation
- Expected:
- ✅ Redirect to login when save attempted
- ❌ NO error shown
Error Flow Comparison
Before Fix:
User creates conversation with expired token
↓
fetch('/api/v1/conversations?agent_id=...', { ... })
↓
Backend returns 401
↓
response.ok === false
↓
❌ Logs error: "Failed to create conversation: 401..."
❌ Returns null
❌ User stuck on broken page
After Fix:
User creates conversation with expired token
↓
fetchWithAuth('/api/v1/conversations?agent_id=...', { ... })
↓
Backend returns 401
↓
fetchWithAuth detects response.status === 401
↓
Calls useAuthStore.getState().logout('expired')
↓
✅ Redirects to /login?session_expired=true
✅ Shows session expired banner
✅ User understands what happened
Console Messages
Success Indicators:
Chat API: 401 detected, triggering logout
AuthGuard: Invalid or missing token, logging out
Should NOT See:
❌ Failed to create conversation: 401 {"error":...}
❌ Failed to load conversation messages
❌ POST http://localhost:3002/api/v1/conversations?agent_id=... 401 (Unauthorized)
(error message should still appear in Network tab but handled gracefully)
Architecture Notes
Current State (Phase 1):
- ✅ Quick fix implemented
- ✅ All 8 conversation operations protected
- ✅ Single helper function (DRY principle)
- ⚠️ Still uses direct
fetch()(not ideal)
Future Enhancement (Phase 2-3):
Migrate to service layer:
// Instead of:
const response = await fetchWithAuth('/api/v1/conversations', {...});
// Use:
import { createConversation } from '@/services/conversations';
const result = await createConversation({ agent_id: agentId });
Benefits of migration:
- Consistent with rest of codebase (agents, datasets use service layer)
- Automatic tenant/auth header injection
- TypeScript type safety
- Cleaner error handling
TODO marker added in helper function for future refactoring.
Related Fixes
This complements earlier session timeout work:
- Session Timeout Redirect Fix - General 401 handling in API layer
- Chat Service 401 Fix - Streaming chat completion errors
- JWT Parsing Protection - parseTokenPayload null safety
- Conversation API 401 Fix - This fix (conversation operations)
Together, these ensure all API endpoints properly handle expired tokens:
- ✅ Core API layer (
api.ts) - General requests - ✅ Chat streaming (
chat-service.ts) - Streaming completions - ✅ Conversation operations (
chat/page.tsx) - Conversation CRUD - ✅ React Query retries (
providers.tsx) - Query failures
Files Modified
- ✅
apps/tenant-app/src/app/chat/page.tsx- Added
fetchWithAuthhelper (lines 98-115) - Replaced 8
fetch()calls withfetchWithAuth()
- Added
Total changes: ~17 lines (1 function + 8 one-word replacements) Risk level: Very low (minimal changes, defensive wrapper) Status: ✅ Complete, running in Docker with hot reload
Verification
Run this in browser console after fix:
// Verify fetchWithAuth exists
console.log(typeof fetchWithAuth); // Should log "function"
// Test conversation creation with bad token
localStorage.setItem('gt2_token', 'invalid');
// Send first chat message
// Expected: Redirect to login, not error in chat
Last Updated: January 2025 Implementation Time: 20 minutes Docker Container: gentwo-tenant-frontend (hot reload active) Related Issues: Session timeout, 401 handling, JWT parsing
Summary
All conversation API operations now properly detect 401 responses and redirect users to login with a clear session expired message. The fix is minimal, maintainable, and consistent with the broader session timeout handling implemented across the application.
Next recommended step: Phase 2-3 migration to service layer for long-term architectural consistency.