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.
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)
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.