How We Built an Autonomous Build Self-Healing Engine
The most common reason AI-generated apps fail to deploy is missing dependencies. Cursor, Lovable, and Bolt.new all regularly import packages without adding them to package.json, and the resulting build failures produce cryptic stderr that vibe coders cannot interpret. We built the AutoRepairService to handle this problem at the platform level: when a build fails, the service traps the stderr, classifies the error, identifies the missing packages, injects them into package.json, and retries the build. Up to two retry attempts are made before the platform gives up and surfaces the error. In the majority of cases, the second build succeeds, and the app goes live without the developer touching a terminal. Here is a deep dive into how we built it, the architecture decisions we made, and the lessons we learned.
The direct answer is that the AutoRepairService is a bounded loop with three components: an error classifier (powered by Gemini 2.5 Flash with a regex fallback), a patcher (that modifies package.json and package-lock.json), and a retry orchestrator (that re-runs the build up to two times). The classifier determines what kind of error occurred (missing package, version mismatch, syntax error, etc.), the patcher applies the appropriate fix, and the retry orchestrator re-runs the build and checks if it succeeded. The loop is transparent: every step is logged, and the developer can review the patches before accepting them into their main branch.
Why We Built It
Before the AutoRepairService, the most common support ticket we received was "my build failed with this error, what do I do?" The error was almost always a missing package: Cannot find module 'clsx', Cannot find module 'lucide-react', Cannot find module '@radix-ui/react-slot'. These errors are trivial to fix (run npm install
We realized that this was a platform problem, not a user problem. The platform has all the information it needs to fix the error: the stderr tells you which package is missing, the package.json tells you what is installed, and the npm install command is deterministic. The only thing missing was the logic to tie them together. So we built the AutoRepairService.
The Architecture
The AutoRepairService has three main components: the error classifier, the patcher, and the retry orchestrator.
1. The Error Classifier
The error classifier takes the build stderr as input and returns a structured classification: what kind of error is this, what packages are missing, what is the recommended fix. The classifier uses a two-layer approach: Gemini 2.5 Flash first, with a regex fallback.
Layer 1: Gemini 2.5 Flash
We use Gemini 2.5 Flash to classify the error, because it is fast (under 2 seconds for typical stderr), cheap (fractions of a cent per classification), and accurate (it correctly identifies missing packages in over 95 percent of cases). The prompt is structured: we give the model the last 200 lines of stderr, and ask it to return a JSON object with the following fields:
{
"error_type": "missing_package" | "version_mismatch" | "syntax_error" | "other",
"missing_packages": ["clsx", "lucide-react"],
"recommended_fix": "npm install clsx lucide-react",
"confidence": 0.95
}The model is remarkably good at this task, because the error patterns are well-represented in its training data. Cannot find module 'clsx' is unambiguous, and the model knows that the fix is to install clsx.
Layer 2: Regex Fallback
If Gemini is unavailable (API outage, rate limit) or returns a low-confidence classification (confidence < 0.7), we fall back to a regex-based classifier. The regex patterns are hand-crafted for the most common error formats:
- Cannot find module '([^']+)' -> missing package
- Module not found: Error: Can't resolve '([^']+)' -> missing package
- npm error: ERESOLVE unable to resolve dependency tree -> version mismatch
The regex fallback is less accurate than Gemini (it only catches the most common patterns), but it is fast and reliable. In practice, the Gemini classifier handles 95 percent of cases, and the regex fallback handles the remaining 5 percent.
2. The Patcher
The patcher takes the classification and applies the recommended fix. For a missing package classification, the patcher:
- Reads package.json from the build context.
- Adds the missing packages to the dependencies (or devDependencies for test/build tools).
- Runs npm install to update package-lock.json.
- Logs the changes for the developer to review.
The patcher does not modify the developer's source repository. The changes are applied to the build context, which is a temporary copy of the repository. The developer can review the patches in the build log and choose to merge them into their main branch manually.
3. The Retry Orchestrator
The retry orchestrator re-runs the build after the patcher has applied the fix. It runs the same build command (npm run build for Next.js, etc.) and checks if it succeeded. If the build succeeds, the orchestrator reports success and proceeds with the deployment. If the build fails again, the orchestrator feeds the new stderr back to the classifier and repeats the loop, up to a maximum of two retry attempts.
The retry limit is important: without it, the AutoRepairService could loop forever on an unfixable error, burning build resources. Two retries handle the common case where multiple packages are missing (the first retry catches one, the second catches another), while preventing infinite loops on unfixable errors.
Step-by-Step: What Happens When a Build Fails
Here is what happens when a typical AI-generated Next.js app fails to build due to a missing package.
Step 1: The build fails
The build runs npm run build, which fails with:
Failed to compile.
./src/components/Button.tsx
Module not found: Error: Can't resolve 'clsx' in '/app/src/components'Step 2: The AutoRepairService traps the error
The build system catches the failure and passes the stderr to the AutoRepairService.
Step 3: The classifier runs
Gemini 2.5 Flash classifies the error:
{
"error_type": "missing_package",
"missing_packages": ["clsx"],
"recommended_fix": "npm install clsx",
"confidence": 0.98
}Step 4: The patcher runs
The patcher adds clsx to package.json:
"dependencies": {
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
+ "clsx": "^2.1.0"
}The patcher runs npm install to update package-lock.json.
Step 5: The retry orchestrator runs
The orchestrator re-runs npm run build. This time, the build succeeds.
Step 6: The deployment proceeds
The build output is deployed to a container, the container starts, and the app is live. The developer sees a banner in the dashboard:
Build failed due to missing clsx. Auto-installed clsx and resumed deployment. Build succeeded on attempt 2.The developer can review the patch (the added clsx dependency) and choose to merge it into their main branch.
Lessons Learned
Building the AutoRepairService taught us several lessons about autonomous build repair:
1. Bounded loops are essential
Without a retry limit, the AutoRepairService could loop forever on an unfixable error. Two retries handle the common case while preventing infinite loops.
2. Transparency builds trust
Every step of the AutoRepairService is logged and visible to the developer. This transparency is essential for trust: the developer knows exactly what the service did, and they can review the patches before accepting them.
3. LLM plus regex is the right architecture
Gemini 2.5 Flash is accurate for most cases, but it is not always available (API outages, rate limits). The regex fallback ensures the service keeps working even when Gemini is down. The two-layer approach gives us both accuracy and reliability.
4. Do not modify the source repository
The AutoRepairService applies patches to the build context, not to the developer's source repository. This means the developer's code is never modified without their consent, and they can review and accept the patches manually.
5. Surface the patches for review
The patches are surfaced in the dashboard, so the developer can see exactly what was changed. This is important for accountability: the developer knows what was added to their package.json, and they can verify that the added packages are legitimate.
The Bounded Loop: What AutoRepair Cannot Do
The AutoRepairService is bounded by design. It handles missing npm packages and lockfile corruption. It does not:
- Rewrite application logic
- Fix syntax errors in the developer's code
- Patch breaking changes in major framework versions
- Migrate deprecated APIs
If the build fails because of one of these, the AutoRepairService surfaces the raw error with a clear explanation, and the developer takes it from there. This is the honest boundary: the service fixes the boring, repetitive failure modes that kill vibe coder momentum, and it leaves the genuinely creative debugging to the developer and their AI assistant.
Scaling the AutoRepairService
As Deployxa has grown, the AutoRepairService has needed to scale to handle thousands of builds per day. The first scaling challenge was Gemini API rate limits. Gemini 2.5 Flash has a rate limit (typically 60 requests per minute per project), which can be exceeded during peak build times. The fix was to implement a request queue that batches classification requests and handles rate limit errors with exponential backoff. The second scaling challenge was build context size. For large monorepos, the build context can be hundreds of megabytes, which makes the patching step slow. The fix was to optimize the patching to only modify the files that need changing, rather than rewriting the entire build context. The third scaling challenge was concurrent retries. When multiple builds fail simultaneously, the retry orchestrator needs to handle them concurrently without interfering with each other. The fix was to use isolated build contexts for each retry, so concurrent retries do not share state. The fourth scaling challenge was observability. As the number of repairs grew, it became hard to track which repairs were successful and which failed. The fix was to build a dashboard that shows repair statistics (success rate, average retry count, most common missing packages), which helps identify patterns and improve the classifier. The fifth scaling challenge was false positives. As the classifier handled more diverse error patterns, it occasionally misclassified errors (e.g., identifying a syntax error as a missing package), which caused the patcher to apply the wrong fix. The fix was to add a verification step that checks whether the patched build actually succeeds, and to fall back to the original error if the patch does not help.
Future Directions
The AutoRepairService is an evolving system, and we have several improvements planned. The first is support for Python projects. Currently, the service handles npm packages, but Python projects have similar issues (missing requirements.txt entries, pip install failures). We are extending the classifier to handle Python error patterns and the patcher to modify requirements.txt and pyproject.toml. The second is support for Go projects. Go's module system is different from npm and pip, but similar issues (missing go.mod entries) can occur. We are extending the classifier to handle Go build errors. The third is proactive repair. Currently, the service reacts to build failures, but we are exploring proactive analysis that identifies potential issues before the build runs (e.g., scanning imports and checking against package.json before the build starts). The fourth is repair persistence. Currently, repairs are applied to the build context but not to the source repository, which means the same issue can recur on the next build. We are exploring an option to automatically create a pull request with the repair, so the fix is persisted to the source repository. The fifth is repair sharing. If multiple apps have the same missing dependency, the service could learn from previous repairs and apply the fix proactively. These improvements are guided by the data we collect from repair attempts, which tells us which error patterns are most common and which repairs are most effective.
Conclusion: Build Repair as a Platform Feature
The AutoRepairService is one example of a broader pattern: AI-native platform features that handle the boring, repetitive failure modes of AI-assisted development. The service is bounded, transparent, and reliable, and it eliminates the most common cause of build failures for AI-generated apps. For vibe coders, this means fewer failed builds and more shipped apps. For the platform, it means fewer support tickets and happier users.
Ready to try build self-healing for yourself? Drag your project to Deployxa Drop for an instant live preview, or install the CLI with npm i -g @deployxa/cli and deploy from your terminal. For more on Deployxa's engineering, see our free developer tools and read about the localhost trap and Next.js build errors.