Skip to content

Commit c654f90

Browse files
committed
🤖 fix: graceful fallback and better logging for workspace name generation
When workspace name generation fails (e.g., proxy returns invalid JSON): - Fall back to placeholder name instead of blocking workspace creation - Show warning toast with error details - Log raw response body for debugging proxy issues Also: - Add warning toast type with theme-aware colors - Explicitly disable extended thinking for generateObject calls
1 parent 3581ebf commit c654f90

File tree

5 files changed

+54
-24
lines changed

5 files changed

+54
-24
lines changed

‎src/browser/components/ChatInput/useCreationWorkspace.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ export function useCreationWorkspace({
131131
const pendingInputKey = getInputKey(getPendingScopeId(projectPath));
132132
updatePersistedState(pendingInputKey, "");
133133
}
134+
135+
// Show warning toast if name generation had issues (e.g., proxy errors)
136+
// Workspace was still created with placeholder name
137+
const warning = "warning" in result ? (result.warning as string | undefined) : undefined;
138+
if (warning) {
139+
setToast({
140+
id: Date.now().toString(),
141+
type: "warning",
142+
title: "Workspace Created",
143+
message: warning,
144+
});
145+
}
146+
134147
// Settings are already persisted via useDraftWorkspaceSettings
135148
// Notify parent to switch workspace (clears input via parent unmount)
136149
onWorkspaceCreated(result.metadata);

‎src/browser/components/ChatInputToast.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import type { ReactNode } from "react";
22
import React, { useEffect, useCallback } from "react";
33
import { cn } from "@/common/lib/utils";
44

5-
const toastTypeStyles: Record<"success" | "error", string> = {
5+
const toastTypeStyles: Record<"success" | "error" | "warning", string> = {
66
success: "bg-toast-success-bg border border-accent-dark text-toast-success-text",
77
error: "bg-toast-error-bg border border-toast-error-border text-toast-error-text",
8+
warning: "bg-toast-warning-bg border border-toast-warning-border text-toast-warning-text",
89
};
910

1011
export interface Toast {
1112
id: string;
12-
type: "success" | "error";
13+
type: "success" | "error" | "warning";
1314
title?: string;
1415
message: string;
1516
solution?: ReactNode;

‎src/browser/styles/globals.css‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@
223223
--color-toast-error-bg: hsl(5 89% 60% / 0.15); /* #f14836 with 15% opacity */
224224
--color-toast-error-text: hsl(5 89% 60%); /* #f14836 */
225225
--color-toast-error-border: hsl(5 89% 60%); /* #f14836 */
226+
--color-toast-warning-bg: hsl(45 100% 51% / 0.15); /* warning yellow with 15% opacity */
227+
--color-toast-warning-text: hsl(45 100% 51%); /* warning yellow */
228+
--color-toast-warning-border: hsl(45 100% 51%); /* warning yellow */
226229
--color-toast-fatal-bg: hsl(0 33% 18%); /* #2d1f1f - fatal error bg */
227230
--color-toast-fatal-border: hsl(0 36% 26%); /* #5a2c2c - fatal error border */
228231

@@ -442,6 +445,9 @@
442445
--color-toast-error-bg: hsl(5 80% 55% / 0.18);
443446
--color-toast-error-text: hsl(5 78% 46%);
444447
--color-toast-error-border: hsl(5 78% 46%);
448+
--color-toast-warning-bg: hsl(45 100% 50% / 0.18);
449+
--color-toast-warning-text: hsl(45 100% 35%);
450+
--color-toast-warning-border: hsl(45 100% 35%);
445451
--color-toast-fatal-bg: hsl(0 72% 94%);
446452
--color-toast-fatal-border: hsl(0 74% 82%);
447453

@@ -662,6 +668,9 @@
662668
--color-toast-error-bg: hsla(1 79% 53% / 0.18);
663669
--color-toast-error-text: #dc322f;
664670
--color-toast-error-border: #dc322f;
671+
--color-toast-warning-bg: hsla(45 100% 35% / 0.18);
672+
--color-toast-warning-text: #b58900;
673+
--color-toast-warning-border: #b58900;
665674
--color-toast-fatal-bg: #fce8e7;
666675
--color-toast-fatal-border: #e8a5a3;
667676

@@ -867,6 +876,9 @@
867876
--color-toast-error-bg: hsla(1 79% 53% / 0.15);
868877
--color-toast-error-text: #dc322f;
869878
--color-toast-error-border: #dc322f;
879+
--color-toast-warning-bg: hsla(45 100% 35% / 0.15);
880+
--color-toast-warning-text: #b58900;
881+
--color-toast-warning-border: #b58900;
870882
--color-toast-fatal-bg: #2d1f1f;
871883
--color-toast-fatal-border: #5a2c2c;
872884

‎src/node/services/ipcMain.ts‎

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ export class IpcMain {
301301
trunkBranch?: string;
302302
}
303303
): Promise<
304-
| { success: true; workspaceId: string; metadata: FrontendWorkspaceMetadata }
304+
| { success: true; workspaceId: string; metadata: FrontendWorkspaceMetadata; warning?: string }
305305
| Result<void, SendMessageError>
306306
> {
307307
// Generate IDs and placeholder upfront for immediate UI feedback
@@ -333,33 +333,26 @@ export class IpcMain {
333333

334334
try {
335335
// 1. Generate workspace branch name using AI (SLOW - but user sees pending state)
336+
// Falls back to placeholder name if AI fails - don't block workspace creation
336337
let branchName: string;
338+
let nameGenerationWarning: string | undefined;
337339
{
338-
const isErrLike = (v: unknown): v is { type: string } =>
339-
typeof v === "object" && v !== null && "type" in v;
340340
const nameResult = await generateWorkspaceName(message, options.model, this.aiService);
341-
if (!nameResult.success) {
342-
const err = nameResult.error;
343-
// Clear pending state on error
344-
session.emitMetadata(null);
345-
if (isErrLike(err)) {
346-
return Err(err);
347-
}
348-
const toSafeString = (v: unknown): string => {
349-
if (v instanceof Error) return v.message;
350-
try {
351-
return JSON.stringify(v);
352-
} catch {
353-
return String(v);
354-
}
355-
};
356-
const msg = toSafeString(err);
357-
return Err({ type: "unknown", raw: `Failed to generate workspace name: ${msg}` });
341+
if (nameResult.success) {
342+
branchName = nameResult.data;
343+
} else {
344+
// Fall back to placeholder name - don't block workspace creation
345+
branchName = generatePlaceholderName(message);
346+
const errMsg =
347+
nameResult.error.type === "unknown" && "raw" in nameResult.error
348+
? nameResult.error.raw
349+
: `Name generation failed: ${nameResult.error.type}`;
350+
nameGenerationWarning = errMsg;
351+
log.info("Falling back to placeholder name", { branchName, error: nameResult.error });
358352
}
359-
branchName = nameResult.data;
360353
}
361354

362-
log.debug("Generated workspace name", { branchName });
355+
log.debug("Generated workspace name", { branchName, warning: nameGenerationWarning });
363356

364357
// 2. Get trunk branch (use provided trunkBranch or auto-detect)
365358
const branches = await listLocalBranches(projectPath);
@@ -478,6 +471,7 @@ export class IpcMain {
478471
success: true,
479472
workspaceId,
480473
metadata: completeMetadata,
474+
warning: nameGenerationWarning,
481475
};
482476
} catch (error) {
483477
const errorMessage = error instanceof Error ? error.message : String(error);

‎src/node/services/workspaceTitleGenerator.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,17 @@ export async function generateWorkspaceName(
5454
return Ok(validateBranchName(result.object.name));
5555
} catch (error) {
5656
const messageText = error instanceof Error ? error.message : String(error);
57+
58+
// Log the raw response body if available (helps debug proxy issues)
59+
const responseBody =
60+
error && typeof error === "object" && "responseBody" in error
61+
? (error as { responseBody?: unknown }).responseBody
62+
: undefined;
63+
if (responseBody) {
64+
log.error("Failed to generate workspace name - raw response:", responseBody);
65+
}
5766
log.error("Failed to generate workspace name with AI", error);
67+
5868
return Err({ type: "unknown", raw: `Failed to generate workspace name: ${messageText}` });
5969
}
6070
}

0 commit comments

Comments
 (0)