Fix webhook error handling with reliable error display

- API now properly detects N8N errors in 200 responses (OpenAI quota, etc.)
- Returns 400 status with simple "Agent unavailable" message for webhook failures
- Frontend shows errors in results container instead of disappearing toast notifications
- Users now see clear, persistent error messages when agents fail
- Keeps success toasts working, only fixes error display

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude 2025-08-15 12:38:43 +05:30
parent a46b07e371
commit 838927f316
3 changed files with 65 additions and 13 deletions

View File

@ -102,18 +102,35 @@ def execute_agent(request):
# Store webhook response
execution.webhook_response = response.json() if response.headers.get('content-type', '').startswith('application/json') else {'raw': response.text}
if response.status_code == 200:
# Check if response contains N8N error indicators
has_error = False
if response.status_code == 200 and execution.webhook_response:
# Check for N8N error patterns
if isinstance(execution.webhook_response, dict):
if 'errorMessage' in execution.webhook_response or 'error' in execution.webhook_response:
has_error = True
if response.status_code == 200 and not has_error:
execution.status = 'completed'
execution.output_data = execution.webhook_response
execution.completed_at = timezone.now()
execution.save()
serializer = AgentExecutionSerializer(execution)
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
execution.status = 'failed'
execution.error_message = f"Webhook returned {response.status_code}: {response.text[:500]}"
if has_error:
error_msg = execution.webhook_response.get('errorMessage', 'Webhook execution failed')
execution.error_message = f"N8N Error: {error_msg[:500]}"
else:
execution.error_message = f"Webhook returned {response.status_code}: {response.text[:500]}"
execution.completed_at = timezone.now()
execution.save()
execution.completed_at = timezone.now()
execution.save()
serializer = AgentExecutionSerializer(execution)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response({
'error': 'Agent is temporarily unavailable. Please try again later.'
}, status=status.HTTP_400_BAD_REQUEST)
except requests.RequestException as e:
execution.status = 'failed'

View File

@ -57,7 +57,7 @@ class AgentsCore extends WorkflowsCore {
} catch (error) {
console.error('Form submission error:', error);
this.constructor.hideProcessing();
this.constructor.showToast('❌ Connection error. Please try again.', 'error');
this.showErrorMessage('❌ Agent is temporarily unavailable. Please try again later.');
this.resetSubmitButton();
}
}
@ -84,7 +84,7 @@ class AgentsCore extends WorkflowsCore {
} catch (error) {
console.error('Agent execution error:', error);
this.constructor.hideProcessing();
this.constructor.showToast(`${error.message}`, 'error');
this.showErrorMessage('❌ Agent is temporarily unavailable. Please try again later.');
this.resetSubmitButton();
}
}
@ -545,6 +545,36 @@ class AgentsCore extends WorkflowsCore {
}
}
/**
* Show error message in results container
*/
showErrorMessage(message) {
const resultsContainer = document.getElementById('resultsContainer');
const resultsContent = document.getElementById('resultsContent');
if (resultsContainer && resultsContent) {
resultsContent.innerHTML = `
<div style="
background: #fee2e2;
border: 1px solid #fecaca;
border-radius: 8px;
padding: 20px;
text-align: center;
color: #dc2626;
font-size: 16px;
font-weight: 500;
margin: 20px 0;
">
${message}
</div>
`;
// Show results container
resultsContainer.style.display = 'block';
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
/**
* Reset submit button to original state
*/

View File

@ -32,8 +32,9 @@ class WorkflowsCore {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
z-index: 99999;
max-width: 400px;
pointer-events: none;
`;
// Safely append to body
@ -61,6 +62,9 @@ class WorkflowsCore {
display: flex;
align-items: center;
animation: slideIn 0.3s ease-out;
pointer-events: auto;
position: relative;
z-index: 100000;
`;
// Add icon and message
@ -94,12 +98,13 @@ class WorkflowsCore {
toastContainer.appendChild(toast);
// Auto-remove after 5 seconds
// Auto-remove after longer time for errors
const timeout = type === 'error' ? 8000 : 5000; // 8 seconds for errors, 5 for others
setTimeout(() => {
if (toast.parentElement) {
toast.remove();
}
}, 5000);
}, timeout);
}
/**