darkwood/ia-exception-bundle
Symfony bundle that enriches HTTP 500 error pages with AI-powered exception analysis via Symfony AI. Replaces raw stack traces with clear explanations, likely causes, and fix suggestions. Supports caching and optional async loading to avoid blocking requests.
composer require symfony/ai-bundle darkwood/ia-exception-bundle
config/packages/ai.yaml:
ai:
platform:
openai:
api_key: '%env(OPENAI_API_KEY)%'
config/bundles.php:
Darkwood\IaExceptionBundle\DarkwoodIaExceptionBundle::class => ['all' => true],
config/packages/darkwood_ia_exception.yaml:
darkwood_ia_exception:
enabled: true
only_status_codes: [500]
throw new \RuntimeException('Test error')).Debugging a Production 500 Error:
DatabaseConnectionException.AI Analysis:
- Probable Cause: MySQL server is unreachable (check DB_HOST in .env).
- Suggested Fix: Restart the database service or verify credentials.
Synchronous Analysis (Default):
kernel.exception event fires for status codes in only_status_codes.Asynchronous Analysis (Production):
async: true in config./__ai_exception/{error_id} via AJAX.kernel.exception is risky.Caching Strategy:
(exception_class, message, top_frames).Laravel Adaptation:
ExceptionListener with Laravel’s App\Exceptions\Handler::render().queue: system for async analysis:
// In App\Exceptions\Handler
public function render($request, Throwable $exception)
{
if ($exception instanceof \RuntimeException && $request->wantsJson()) {
AiExceptionAnalysis::dispatch($exception)->delay(now()->addSeconds(5));
return response()->json(['error' => 'Analysis queued'], 500);
}
}
Symfony-Specific:
error.html.twig to include AI analysis:
{% if exception.analysis %}
<div class="ai-analysis">
<h3>AI Analysis</h3>
<p>{{ exception.analysis.explanation }}</p>
</div>
{% endif %}
Darkwood\IaExceptionBundle\EventListener\ExceptionListener to add custom logic (e.g., filter sensitive data).Async Frontend:
/__ai_exception/{id}:
document.addEventListener('DOMContentLoaded', () => {
fetch(`/__ai_exception/${errorId}`)
.then(response => response.json())
.then(data => {
document.getElementById('ai-analysis').innerHTML = `
<h4>Probable Causes</h4>
<ul>${data.probable_causes.map(c => `<li>${c}</li>`).join('')}</ul>
`;
});
});
| Scenario | Configuration Snippet | Notes |
|---|---|---|
| Dev Mode | include_trace: true |
Exposes stack traces for debugging. |
| Production | async: true, cache_ttl: 3600 |
Non-blocking, cached for 1 hour. |
| Cost Optimization | cache_ttl: 86400, timeout_ms: 500 |
Reduce API calls, faster response. |
| Multi-LLM Support | Extend Darkwood\IaExceptionBundle\Agent\AgentInterface |
Add custom AI providers (e.g., Mistral). |
Sensitive Data Exposure:
include_trace: true) may leak file paths, method names, or environment variables.include_trace: false in production. Sanitize exception messages:
// In a custom event subscriber
$message = preg_replace('/\b(password|token|secret)\b/i', '[REDACTED]', $exception->getMessage());
AI Hallucinations:
confidence score to filter low-confidence responses:
darkwood_ia_exception:
min_confidence: 0.7 # Add this to config (requires bundle update)
Async Race Conditions:
async_context_ttl (min 60s) to ensure context persists.if (time() - $context['created_at'] > $config['async_context_ttl']) {
throw new \RuntimeException('Context expired');
}
Caching Quirks:
# Customize the cache key logic in a service
services:
Darkwood\IaExceptionBundle\Cache\ExceptionCache:
arguments:
$cacheKeyGenerator: '@app.ai_exception_cache_key_generator'
Symfony AI Bundle Dependencies:
symfony/ai-bundle may break this bundle.composer.json:
"require": {
"symfony/ai-bundle": "^0.8",
"darkwood/ia-exception-bundle": "^1.3"
}
Log AI Requests/Responses:
framework:
http_client:
logging: true
var/log/dev.log for AI API calls.Test Async Flow:
darkwood_ia_exception:
timeout_ms: 2000 # Force timeout
Inspect Cache:
$cache = $container->get('cache.app');
$keys = $cache->getIterator()->getKeys();
Validate JSON Output:
/__ai_exception/{id} endpoint directly:
curl -H "Accept: application/json" http://localhost/__ai_exception/abc123
Custom AI Agents:
Darkwood\IaExceptionBundle\Agent\AgentInterface to support custom LLMs:
namespace App\Agent;
use Darkwood\IaExceptionBundle\Agent\AgentInterface;
class MistralAgent implements AgentInterface {
public function analyze(Throwable $exception): array {
// Call Mistral API
return ['explanation' => 'Mistral-generated fix...'];
}
}
services.yaml:
services:
App\Agent\MistralAgent: ~
Dynamic Status Codes:
only_status_codes per environment:
when@prod:
darkwood_ia_exception:
only_status_codes: [500, 503]
Post-Analysis Hooks:
darkwood_ia_exception.analyzed eventHow can I help you explore Laravel packages today?