Skip to content

Commit

Permalink
feat: catch errors from web container preview and show in actionable …
Browse files Browse the repository at this point in the history
…alert so user can send them to AI for fixing (stackblitz-labs#856)

* Catch errors from web container

* Show fix error popup on errors in preview

* Remove unneeded action type

* PR comments

* Cleanup urls in stacktrace

---------

Co-authored-by: Anirban Kar <[email protected]>
  • Loading branch information
2 people authored and Stijnus committed Dec 26, 2024
1 parent 5c69804 commit d5324df
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMed
- ✅ Detect package.json and commands to auto install & run preview for folder and git import (@wonderwhy-er)
- ✅ Selection tool to target changes visually (@emcconnell)
- ✅ Detect terminal Errors and ask bolt to fix it (@thecodacus)
- ✅ Detect preview Errors and ask bolt to fix it (@wonderwhy-er)
- ✅ Add Starter Template Options (@thecodacus)
-**HIGH PRIORITY** - Prevent bolt from rewriting files as often (file locking and diffs)
-**HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start)
Expand Down
22 changes: 14 additions & 8 deletions app/components/chat/ChatAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ interface Props {
}

export default function ChatAlert({ alert, clearAlert, postMessage }: Props) {
const { description, content } = alert;
const { description, content, source } = alert;

const isPreview = source === 'preview';
const title = isPreview ? 'Preview Error' : 'Terminal Error';
const message = isPreview
? 'We encountered an error while running the preview. Would you like Bolt to analyze and help resolve this issue?'
: 'We encountered an error while running terminal commands. Would you like Bolt to analyze and help resolve this issue?';

return (
<AnimatePresence>
Expand Down Expand Up @@ -38,19 +44,15 @@ export default function ChatAlert({ alert, clearAlert, postMessage }: Props) {
transition={{ delay: 0.1 }}
className={`text-sm font-medium text-bolt-elements-textPrimary`}
>
{/* {title} */}
Opps There is an error
{title}
</motion.h3>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
className={`mt-2 text-sm text-bolt-elements-textSecondary`}
>
<p>
We encountered an error while running terminal commands. Would you like Bolt to analyze and help resolve
this issue?
</p>
<p>{message}</p>
{description && (
<div className="text-xs text-bolt-elements-textSecondary p-2 bg-bolt-elements-background-depth-3 rounded mt-4 mb-4">
Error: {description}
Expand All @@ -67,7 +69,11 @@ export default function ChatAlert({ alert, clearAlert, postMessage }: Props) {
>
<div className={classNames(' flex gap-2')}>
<button
onClick={() => postMessage(`*Fix this error on terminal* \n\`\`\`sh\n${content}\n\`\`\`\n`)}
onClick={() =>
postMessage(
`*Fix this ${isPreview ? 'preview' : 'terminal'} error* \n\`\`\`${isPreview ? 'js' : 'sh'}\n${content}\n\`\`\`\n`,
)
}
className={classNames(
`px-2 py-1.5 rounded-md text-sm font-medium`,
'bg-bolt-elements-button-primary-background',
Expand Down
28 changes: 26 additions & 2 deletions app/lib/webcontainer/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WebContainer } from '@webcontainer/api';
import { WORK_DIR_NAME } from '~/utils/constants';
import { cleanStackTrace } from '~/utils/stacktrace';

interface WebContainerContext {
loaded: boolean;
Expand All @@ -22,10 +23,33 @@ if (!import.meta.env.SSR) {
import.meta.hot?.data.webcontainer ??
Promise.resolve()
.then(() => {
return WebContainer.boot({ workdirName: WORK_DIR_NAME });
return WebContainer.boot({
workdirName: WORK_DIR_NAME,
forwardPreviewErrors: true, // Enable error forwarding from iframes
});
})
.then((webcontainer) => {
.then(async (webcontainer) => {
webcontainerContext.loaded = true;

const { workbenchStore } = await import('~/lib/stores/workbench');

// Listen for preview errors
webcontainer.on('preview-message', (message) => {
console.log('WebContainer preview message:', message);

// Handle both uncaught exceptions and unhandled promise rejections
if (message.type === 'PREVIEW_UNCAUGHT_EXCEPTION' || message.type === 'PREVIEW_UNHANDLED_REJECTION') {
const isPromise = message.type === 'PREVIEW_UNHANDLED_REJECTION';
workbenchStore.actionAlert.set({
type: 'preview',
title: isPromise ? 'Unhandled Promise Rejection' : 'Uncaught Exception',
description: message.message,
content: `Error occurred at ${message.pathname}${message.search}${message.hash}\nPort: ${message.port}\n\nStack trace:\n${cleanStackTrace(message.stack || '')}`,
source: 'preview',
});
}
});

return webcontainer;
});

Expand Down
1 change: 1 addition & 0 deletions app/types/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ export interface ActionAlert {
title: string;
description: string;
content: string;
source?: 'terminal' | 'preview'; // Add source to differentiate between terminal and preview errors
}
27 changes: 27 additions & 0 deletions app/utils/stacktrace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Cleans webcontainer URLs from stack traces to show relative paths instead
*/
export function cleanStackTrace(stackTrace: string): string {
// Function to clean a single URL
const cleanUrl = (url: string): string => {
const regex = /^https?:\/\/[^\/]+\.webcontainer-api\.io(\/.*)?$/;

if (!regex.test(url)) {
return url;
}

const pathRegex = /^https?:\/\/[^\/]+\.webcontainer-api\.io\/(.*?)$/;
const match = url.match(pathRegex);

return match?.[1] || '';
};

// Split the stack trace into lines and process each line
return stackTrace
.split('\n')
.map((line) => {
// Match any URL in the line that contains webcontainer-api.io
return line.replace(/(https?:\/\/[^\/]+\.webcontainer-api\.io\/[^\s\)]+)/g, (match) => cleanUrl(match));
})
.join('\n');
}

0 comments on commit d5324df

Please sign in to comment.