Do you need to file an issue?
Describe the bug
We discovered two distinct frontend regressions in the 1.4.0 release series. Both are independently reproducible. Reporting together since investigation revealed them in tandem.
Bug 1: Chat input field stays disabled after the first turn (1.4.0)
Version: deeptutor==1.4.0 (PyPI, released 2026-05-22)
Deployment: packaged Next.js frontend launched via deeptutor start → next-server v16.2.3.
Symptom:
After the first user→assistant turn renders correctly, the chat input field becomes permanently disabled. The "Send" button is greyed out. Opening a new chat does not help — input is disabled there too. Hard refresh + incognito + clearing site data do not restore it.
Backend is healthy:
- Turn writes correctly to
data/user/chat_history.db: status completed, assistant message stored (verified e.g. 1431 chars of valid RAG-grounded answer).
- Last event in
turn_events: done with status: completed. Sequence is clean: thinking → content → stage_end → sources → result → done.
journalctl -u deeptutor shows no error / warning at the time of the failed second submit.
Network observations (DevTools):
- WebSocket
/api/v1/ws connection is established and stays open.
wsm.sessionActivated / wsm.sessionDeactivated frames are visible.
POST /api/v1/plugins/capabilities/chat/execute-stream is never issued when the user tries to submit the second message — submit is blocked client-side.
- No outgoing WS frame
{type: "message"} either.
Network/proxy ruled out:
- Reverse proxy: Caddy v2 (no custom WS config needed for Caddy v2).
- Direct
curl POST /api/v1/plugins/capabilities/chat/execute-stream returns 401 {"detail":"Not authenticated"} instantly — SSE not buffered.
- WebSocket connections accept and frames pass through (status events arrive).
Conclusion:
This is a client-side regression in the packaged Next.js bundle's chat-input state machine. The "responding → idle" state transition is never completed after done arrives, leaving the input/send affordances permanently disabled.
Workaround: drive chat via the deeptutor CLI and read responses from data/user/chat_history.db. RAG, tool calling, sessions all work correctly end-to-end via this path.
Bug 2: Beta 1.4.0b0 has a hardcoded URL string as "not configured" sentinel
Version: deeptutor==1.4.0b0 (PyPI, released 2026-05-21)
Symptom:
Frontend immediately errors out on load with:
NEXT_PUBLIC_API_BASE is not configured. Please update data/user/settings/system.json and restart.
Knowledge Bases page shows "No knowledge bases yet" (0). Settings panel doesn't load. No API calls reach the backend.
Root cause (in packaged .next chunks of 1.4.0b0):
A specific URL literal appears to be a developer/staging URL that leaked into the build as a process.env.NEXT_PUBLIC_API_BASE default, and is simultaneously used as a "this value means not-configured" sentinel in the runtime config guard. We discovered this only because our deployment URL happened to collide with that exact literal. The guard logic looks like:
javascript
function(a){
if(!a || "https://<leaked-dev-url>" === a)
throw Error("NEXT_PUBLIC_API_BASE is not configured. Please update data/user/settings/system.json and restart.");
return a;
}("https://<leaked-dev-url>")
Any deployment that legitimately resolves NEXT_PUBLIC_API_BASE to that exact URL hits the guard with a perfectly valid value and the frontend is unusable. This is a build-hygiene issue, not a config bug on the deployer's side — the URL literal is reachable in the public 1.4.0b0 bundle. (Maintainers can locate the exact literal by grepping the 1.4.0b0 chunks for the guard expression above.)
In 1.4.0 final this guard is absent — grep over 1.4.0 chunks returns no match for that comparison expression.
Fix suggestion:
Remove the literal-URL sentinel. If a "not-configured" check is still desired, base it on the unsubstituted placeholder marker (__NEXT_PUBLIC_API_BASE_PLACEHOLDER__) which is what _patch_packaged_web_placeholders in deeptutor/runtime/launcher.py is actually designed to replace.
Environment
deeptutor from PyPI; packaged Next.js frontend, next-server v16.2.3
- Reverse proxy: Caddy v2
- Python 3.11
Versions probed
Version | Released | Chat input bug | URL sentinel bug
-- | -- | -- | --
1.4.0b0 | 2026-05-21 | not tested (sentinel blocks all) | YES
1.4.0 | 2026-05-22 | YES | NO
Related (not part of this report, mentioned for context)
Two non-blocking server-side issues found in 1.4.0 during the same investigation, both with local patches:
- A custom LLM provider missing from
deeptutor/services/llm/capabilities.py:PROVIDER_CAPABILITIES falls back to DEFAULT_CAPABILITIES (supports_tools: False). Result: tool descriptions are injected into the system prompt instead of passed via OpenAI tools=; the model emits XML <tool_call>...</tool_call> in its reasoning trace, the parser ignores them, and RAG never executes — even though the provider natively returns structured tool_calls. Fix: add the provider entry with supports_tools: True. It may be worth making the fallback behavior more visible (e.g. a warning log when a provider is unknown).
deeptutor/services/rag/service.py:_capture_raw_logs.emit returns an unawaited coroutine when the log emit happens off the event loop, triggering RuntimeWarning: coroutine 'RAGService._emit_tool_event' was never awaited. Suggested fix: schedule via asyncio.ensure_future when a running loop exists; otherwise coro.close() and return.
Happy to send these as separate PRs if useful.
Steps to reproduce
See "Describe the bug" above — symptoms, network observations and reproduction details for both regressions are documented there.
Expected Behavior
Bug 1: chat input field should re-enable after the assistant turn completes, allowing further messages. Bug 2: a valid NEXT_PUBLIC_API_BASE should not be rejected by the config guard.
Related Module
Frontend/Web
Configuration Used
See "Describe the bug" above.
Logs and screenshots
No response
Additional Information
- DeepTutor Version: 1.4.0 (and 1.4.0b0, see report)
- Operating System: Linux
- Python Version: 3.11
- Node.js Version: next-server v16.2.3 (packaged)
- Browser: Chrome
- Reverse proxy: Caddy v2
- Additional details: see "Describe the bug" above
Do you need to file an issue?
Describe the bug
We discovered two distinct frontend regressions in the 1.4.0 release series. Both are independently reproducible. Reporting together since investigation revealed them in tandem.
Bug 1: Chat input field stays disabled after the first turn (1.4.0)
Version:
deeptutor==1.4.0(PyPI, released 2026-05-22) Deployment: packaged Next.js frontend launched viadeeptutor start→next-server v16.2.3.Symptom: After the first user→assistant turn renders correctly, the chat input field becomes permanently disabled. The "Send" button is greyed out. Opening a new chat does not help — input is disabled there too. Hard refresh + incognito + clearing site data do not restore it.
Backend is healthy:
data/user/chat_history.db: statuscompleted, assistant message stored (verified e.g. 1431 chars of valid RAG-grounded answer).turn_events:donewithstatus: completed. Sequence is clean:thinking → content → stage_end → sources → result → done.journalctl -u deeptutorshows no error / warning at the time of the failed second submit.Network observations (DevTools):
/api/v1/wsconnection is established and stays open.wsm.sessionActivated/wsm.sessionDeactivatedframes are visible.POST /api/v1/plugins/capabilities/chat/execute-streamis never issued when the user tries to submit the second message — submit is blocked client-side.{type: "message"}either.Network/proxy ruled out:
curl POST /api/v1/plugins/capabilities/chat/execute-streamreturns401 {"detail":"Not authenticated"}instantly — SSE not buffered.Conclusion: This is a client-side regression in the packaged Next.js bundle's chat-input state machine. The "responding → idle" state transition is never completed after
donearrives, leaving the input/send affordances permanently disabled.Workaround: drive chat via the
deeptutorCLI and read responses fromdata/user/chat_history.db. RAG, tool calling, sessions all work correctly end-to-end via this path.Bug 2: Beta 1.4.0b0 has a hardcoded URL string as "not configured" sentinel
Version:
deeptutor==1.4.0b0(PyPI, released 2026-05-21)Symptom: Frontend immediately errors out on load with:
Knowledge Bases page shows "No knowledge bases yet" (0). Settings panel doesn't load. No API calls reach the backend.
Root cause (in packaged
.nextchunks of 1.4.0b0):A specific URL literal appears to be a developer/staging URL that leaked into the build as a
process.env.NEXT_PUBLIC_API_BASEdefault, and is simultaneously used as a "this value means not-configured" sentinel in the runtime config guard. We discovered this only because our deployment URL happened to collide with that exact literal. The guard logic looks like:Any deployment that legitimately resolves
NEXT_PUBLIC_API_BASEto that exact URL hits the guard with a perfectly valid value and the frontend is unusable. This is a build-hygiene issue, not a config bug on the deployer's side — the URL literal is reachable in the public 1.4.0b0 bundle. (Maintainers can locate the exact literal by grepping the 1.4.0b0 chunks for the guard expression above.)In 1.4.0 final this guard is absent —
grepover 1.4.0 chunks returns no match for that comparison expression.Fix suggestion: Remove the literal-URL sentinel. If a "not-configured" check is still desired, base it on the unsubstituted placeholder marker (
__NEXT_PUBLIC_API_BASE_PLACEHOLDER__) which is what_patch_packaged_web_placeholdersindeeptutor/runtime/launcher.pyis actually designed to replace.Environment
deeptutorfrom PyPI; packaged Next.js frontend,next-server v16.2.3Versions probed
Related (not part of this report, mentioned for context)
Two non-blocking server-side issues found in 1.4.0 during the same investigation, both with local patches:
deeptutor/services/llm/capabilities.py:PROVIDER_CAPABILITIESfalls back toDEFAULT_CAPABILITIES(supports_tools: False). Result: tool descriptions are injected into the system prompt instead of passed via OpenAItools=; the model emits XML<tool_call>...</tool_call>in its reasoning trace, the parser ignores them, and RAG never executes — even though the provider natively returns structuredtool_calls. Fix: add the provider entry withsupports_tools: True. It may be worth making the fallback behavior more visible (e.g. a warning log when a provider is unknown).deeptutor/services/rag/service.py:_capture_raw_logs.emitreturns an unawaited coroutine when the log emit happens off the event loop, triggeringRuntimeWarning: coroutine 'RAGService._emit_tool_event' was never awaited. Suggested fix: schedule viaasyncio.ensure_futurewhen a running loop exists; otherwisecoro.close()and return.Happy to send these as separate PRs if useful.
Steps to reproduce
See "Describe the bug" above — symptoms, network observations and reproduction details for both regressions are documented there.
Expected Behavior
Bug 1: chat input field should re-enable after the assistant turn completes, allowing further messages. Bug 2: a valid NEXT_PUBLIC_API_BASE should not be rejected by the config guard.
Related Module
Frontend/Web
Configuration Used
See "Describe the bug" above.
Logs and screenshots
No response
Additional Information