666ghj/MiroFish · error · Error
res.error || t('step5.requestFailed')
Error message
res.error || t('step5.requestFailed') What it means
Thrown by sendToReportAgent in Step5Interaction.vue when the backend call to chatWithReport (POST with simulation_id, message, chat_history) resolves with success=false or an empty data payload. This is the app's own request-wrapper convention: any non-2xx or business failure comes back as { success: false, error }, and the component converts that into a thrown Error so sendMessage's catch block can render it in the chat log. The i18n key step5.requestFailed is only the fallback text when the server gave no error string.
Solutions
- Inspect the res.error value and the browser Network tab response for the chatWithReport call to get the real backend reason.
- Verify props.simulationId matches an existing, still-active simulation (check the backend simulation listing endpoint).
- Check backend logs for the report-agent chat route for stack traces (LLM provider errors, missing API keys, timeouts).
- If it is an intermittent timeout from a slow LLM, increase the request timeout for the chat endpoint in the API client.
- If the server returns no error field, improve the backend route to always populate error on failure so users see the true cause instead of the generic i18n fallback.
Example fix
// before
const res = await chatWithReport({ simulation_id: props.simulationId, message, chat_history: historyForApi })
if (res.success && res.data) { /* ... */ } else {
throw new Error(res.error || t('step5.requestFailed'))
}
// after - surface HTTP status detail and rethrow only once
const res = await chatWithReport({ simulation_id: props.simulationId, message, chat_history: historyForApi })
if (!res.success) {
throw new Error(res.error || `Chat request failed (HTTP ${res.status ?? 'unknown'})`)
}
if (!res.data) {
throw new Error(t('step5.noResponse'))
}
chatHistory.value.push({ role: 'assistant', content: res.data.response || res.data.answer || t('step5.noResponse'), timestamp: new Date().toISOString() }) Defensive patterns
Strategy: try-catch
Validate before calling
if (!props.simulationId) {
addLog(t('log.invalidSimulation'))
return
}
const historyForApi = chatHistory.value.slice(0, -1).slice(-10).map(m => ({ role: m.role, content: m.content }))
if (historyForApi.some(m => !m.content)) {
chatHistory.value = chatHistory.value.filter(m => m.content)
} Type guard
function isChatSuccess(res) {
return Boolean(res && res.success === true && res.data && (res.data.response || res.data.answer))
} Try / catch
try {
const res = await chatWithReport({ simulation_id: props.simulationId, message, chat_history: historyForApi })
if (!res.success || !res.data) throw new Error(res.error || t('step5.requestFailed'))
/* push assistant message */
} catch (err) {
addLog(t('log.sendFailed', { error: err.message }))
chatHistory.value.push({ role: 'assistant', content: t('step5.errorOccurred', { error: err.message }), timestamp: new Date().toISOString() })
} finally {
isSending.value = false
} Prevention
- Disable the send button while the simulation is not in a running state so requests cannot target dead simulations.
- Always populate res.error on the backend failure envelope so users see real causes instead of the generic i18n fallback.
- Keep a single catch in sendMessage (already present) and let sendToReportAgent only throw; do not catch twice.
- Verify simulation status on page mount and prompt re-load if it expired.
When it happens
Trigger: Calling chatWithReport({ simulation_id, message, chat_history }) when: the simulation_id does not exist or has expired server-side; the report-generation Celery/LLM task failed; the backend returned 4xx/5xx that the request wrapper flattens into { success: false }; or the response succeeded but data was null/undefined.
Common situations: Session/simulation no longer running (page left open past backend TTL), report agent pipeline crashed mid-generation, backend restarted between page load and chat send, or a proxy/gateway (502/504) intercepting the request during LLM long-polls.
Related errors
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/bdf897173fca40ed.
Report an issue: GitHub.
Appendix: source
Thrown at frontend/src/components/Step5Interaction.vue:708
role: msg.role,
content: msg.content
}))
const res = await chatWithReport({
simulation_id: props.simulationId,
message: message,
chat_history: historyForApi
})
if (res.success && res.data) {
chatHistory.value.push({
role: 'assistant',
content: res.data.response || res.data.answer || t('step5.noResponse'),
timestamp: new Date().toISOString()
})
addLog(t('log.reportAgentReplied'))
} else {
throw new Error(res.error || t('step5.requestFailed'))
}
}
const sendToAgent = async (message) => {
if (!selectedAgent.value || selectedAgentIndex.value === null) {
throw new Error(t('step5.selectAgentFirst'))
}
addLog(t('log.sendToAgent', { name: selectedAgent.value.username, message: message.substring(0, 50) }))
// Build prompt with chat history
let prompt = message
if (chatHistory.value.length > 1) {
const historyContext = chatHistory.value
.slice(0, -1)
.slice(-6)
.map(msg => `${msg.role === 'user' ? '提问者' : '你'}:${msg.content}`)
.join('\n')View on GitHub (pinned to b5b53acc57)