Do you need to file an issue?
Describe the bug
POST /api/v1/book/books/confirm-proposal ("Confirm proposal & build spine" button) reliably fails client-side with a proxy error after ~35-47s, even though the backend keeps working and successfully finishes the spine generation shortly after.
Frontend/Next.js log:
Failed to proxy http://localhost:8001/api/v1/book/books/confirm-proposal Error: socket hang up
at ignore-listed frames {
code: 'ECONNRESET'
}
Backend log: no error, no traceback, no confirm_proposal failed line at all (see book.py — confirm_proposal route logs logger.error(f"confirm_proposal failed: {exc}", exc_info=True) on exception, and this never appears). The backend is still mid-request when the connection drops.
I confirmed this by checking GET /api/v1/book/books on the backend directly after the failures: both affected books ended up in spine_ready / compiling state, with created_at timestamps matching the exact moment of the failed proxy calls. So the backend genuinely finishes the (multi-LLM-call) spine generation — the client/proxy just gives up first and the UI has no way to know it eventually succeeded.
Steps to reproduce
- Create a book from a knowledge base, get a generated proposal.
- Click "Confirm proposal & build spine".
- Wait — after ~35-47s the request errors out client-side (spinner/error, or UI appears stuck).
- Wait another ~30-60s and refresh
My Books — the book has actually reached spine_ready anyway.
Expected Behavior
The UI shouldn't time out / error on a request that the backend is still successfully processing. Two options that seem reasonable given the existing code:
confirmProposal (web/lib/book-api.ts) is a plain blocking POST that the Next.js middleware (web/proxy.ts) rewrites straight through to the backend with no timeout override — but something in the path (browser/OS network stack, most likely an idle-connection timeout with no bytes flowing for 35-47s) resets the connection before the multi-LLM spine-build finishes.
- There's already a WebSocket path for this exact operation —
book.py's /ws handler accepts a {"type": "confirm_proposal", "book_id": ..., "proposal": {...}} message and streams progress back (confirm_proposal_result). The "Confirm proposal & build spine" button in web/app/(workspace)/book/page.tsx (handleConfirmProposal, ~line 287) calls bookApi.confirmProposal() (the blocking REST path) instead of using the already-open book WebSocket. Routing this through the WS path (which the frontend already opens for other book events) would sidestep the long-blocking-request-over-HTTP problem entirely and give real progress feedback instead of a dead spinner.
Related Module
Book Creation
Configuration Used
LLM: openrouter / anthropic/claude-sonnet-4-5
EMBEDDING: openai / text-embedding-3-large
Deployment: Docker (ghcr.io/hkuds/deeptutor:latest)
Logs and screenshots
Frontend container log excerpt:
INFO: 172.22.0.1:0 - "WebSocket /api/v1/book/ws" [accepted]
Failed to proxy http://localhost:8001/api/v1/book/books/confirm-proposal Error: socket hang up
at ignore-listed frames {
code: 'ECONNRESET'
}
Error: socket hang up
at ignore-listed frames {
code: 'ECONNRESET'
}
No corresponding backend-side error/traceback for the same request in the same log window.
Do you need to file an issue?
Describe the bug
POST /api/v1/book/books/confirm-proposal("Confirm proposal & build spine" button) reliably fails client-side with a proxy error after ~35-47s, even though the backend keeps working and successfully finishes the spine generation shortly after.Frontend/Next.js log:
Backend log: no error, no traceback, no
confirm_proposal failedline at all (seebook.py—confirm_proposalroute logslogger.error(f"confirm_proposal failed: {exc}", exc_info=True)on exception, and this never appears). The backend is still mid-request when the connection drops.I confirmed this by checking
GET /api/v1/book/bookson the backend directly after the failures: both affected books ended up inspine_ready/compilingstate, withcreated_attimestamps matching the exact moment of the failed proxy calls. So the backend genuinely finishes the (multi-LLM-call) spine generation — the client/proxy just gives up first and the UI has no way to know it eventually succeeded.Steps to reproduce
My Books— the book has actually reachedspine_readyanyway.Expected Behavior
The UI shouldn't time out / error on a request that the backend is still successfully processing. Two options that seem reasonable given the existing code:
confirmProposal(web/lib/book-api.ts) is a plain blocking POST that the Next.js middleware (web/proxy.ts) rewrites straight through to the backend with no timeout override — but something in the path (browser/OS network stack, most likely an idle-connection timeout with no bytes flowing for 35-47s) resets the connection before the multi-LLM spine-build finishes.book.py's/wshandler accepts a{"type": "confirm_proposal", "book_id": ..., "proposal": {...}}message and streams progress back (confirm_proposal_result). The "Confirm proposal & build spine" button inweb/app/(workspace)/book/page.tsx(handleConfirmProposal, ~line 287) callsbookApi.confirmProposal()(the blocking REST path) instead of using the already-open book WebSocket. Routing this through the WS path (which the frontend already opens for other book events) would sidestep the long-blocking-request-over-HTTP problem entirely and give real progress feedback instead of a dead spinner.Related Module
Book Creation
Configuration Used
LLM: openrouter / anthropic/claude-sonnet-4-5
EMBEDDING: openai / text-embedding-3-large
Deployment: Docker (ghcr.io/hkuds/deeptutor:latest)
Logs and screenshots
Frontend container log excerpt:
No corresponding backend-side error/traceback for the same request in the same log window.