darkwood/ia-exception-bundle
Symfony bundle that replaces raw HTTP 500 stack traces with LLM-generated explanations, probable causes, and suggested fixes via Symfony AI. Configurable status codes, agent, timeout, cache, and async non-blocking analysis for readable, production-safe error pages.
composer require'd into a Laravel app. Its real product value is as a reference design: ~a listener on the exception pipeline, a fingerprint-keyed PSR-6 cache, an async polling endpoint, and a fail-safe that never overrides the 500. That is a 2–3 day spike to port to App\Exceptions\Handler + a queued job, versus adopting an unsupported third-party dependency. The PM decision this informs is "do we copy this pattern or wait for a vendor."include_trace is ever permitted outside local dev. This is a governance artifact you need regardless of whether you adopt the package.cache_ttl fingerprinting (class + message + top frames) and timeout_ms are the levers. PM-relevant decision: what is the acceptable spend per unique error signature and the acceptable worst-case added latency on the error path. The async mode exists precisely because the synchronous kernel callback is a bad place to block.enabled: false), and measurable. Good stepping stone before AI touches customer-visible surfaces.Consider it when all of the following hold:
symfony/ai-bundle already installed and a platform/agent configured. This is a narrow, bleeding-edge window — most production PHP estates are on 8.2–8.4 and will be for a while.darkwood/* versioning scheme (which jumps between 1.x and Symfony-mirroring 8.x lines) as a pinning and upgrade-planning cost.Look elsewhere — or don't build this at all — when:
Handler::render(), i.e., writing it yourself anyway.async: true and a scoped HTTP client enforcing an 800 ms timeout, you are adding a network hop, a new route (/__ai_exception/{id}), and a cache dependency to your failure mode. The fail-safe behavior is genuinely good, but "it degrades gracefully" is not the same as "it can't hurt."Bottom line: do not adopt this package; adopt its design.
For executives: we found a small open-source Symfony bundle that turns raw HTTP 500 stack traces into a plain-language explanation, likely causes, and suggested fixes using an LLM. It directly targets mean-time-to-resolution, which is currently one of the most expensive hours our engineers spend. It is not compatible with our stack — it requires Symfony 8 and PHP 8.5, and we are on Laravel — and it is a two-star, single-maintainer project with no downstream users. My recommendation is a two-to-three day engineering spike to reproduce the pattern in-house, behind a feature flag, scoped to internal tooling. Total cost is the spike plus roughly a few cents per unique error signature, since results are cached. The main risk is not technical but contractual: exception messages would be sent to an LLM provider, so we need a DPA and a written rule that stack traces never leave our environment. Success measure is unambiguous — a reduction in time-to-diagnosis on production incidents, and the share of errors where the generated hypothesis was actually actionable.
For engineering: the bundle is a working reference implementation worth reading before you write ours. Steal these four things: (1) it hooks the exception pipeline and only augments the body — the 500 status is always preserved and it never sets a response on AI failure, so Symfony's default handling runs untouched; that fail-safe posture is non-negotiable for us too. (2) It caches by a hash of exception class, message, and top stack frames, which collapses repeated errors and caps both cost and LLM calls. (3) Its async mode returns the error page immediately with a placeholder and fetches the analysis from a dedicated endpoint, so the blocking network call never sits in the exception handler — in Laravel this maps to a queued job plus a cached result, which is cleaner still. (4) It sends a deliberately minimal payload: class, message, file, line, and trace only when explicitly enabled in dev. Nothing from env, headers, cookies, or request bodies. We should match that allowlist exactly rather than inventing our own.
The port itself is small: a listener or Handler hook, a fingerprint cache key, one queued job calling the LLM, and one internal view plus a JSON endpoint. We should ship it disabled by default and off the customer-facing error page entirely — first surface is an enrichment on our existing alerts, second surface is an internal diagnostics page. If after a month the suggestions are consistently actionable, we revisit promoting it to a supported internal tool; if they are not, we have spent three days and one API key and learned something concrete about where AI is and isn't useful in our stack.
How can I help you explore Laravel packages today?