From c6428e5966f4deaeb925aeeb3feb855d016d85fb Mon Sep 17 00:00:00 2001 From: Dion Low Date: Tue, 21 Oct 2025 14:30:55 -0700 Subject: [PATCH 1/4] fix: prevent config validation errors from crashing parent app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace throwing errors with safe error handling in config validation and OAuth mutations. Changes: - Refactored toCreateConfigContent to return result object with data or error instead of throwing - Updated useCreateInstallation to handle config validation errors via onError callback - Changed useUpdateOauthConnectMutation to use Promise.reject instead of throw for clearer error handling These validation errors now flow through the error callback pattern, preventing parent app crashes when: - Config is missing required provider field - OAuth update is called without required project ID or connection ID 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/headless/config/types.ts | 10 ++++++---- src/headless/installation/useCreateInstallation.ts | 11 ++++++++++- src/hooks/mutation/useUpdateOauthConnectMutation.ts | 8 ++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/headless/config/types.ts b/src/headless/config/types.ts index 371518ea2..73e08b4fb 100644 --- a/src/headless/config/types.ts +++ b/src/headless/config/types.ts @@ -30,15 +30,17 @@ export function toUpdateConfigContent( /** * Helper to convert InstallationConfigContent to ConfigContent - * Throws error if required fields are missing + * Returns result object with either data or error */ export function toCreateConfigContent( config: InstallationConfigContent, -): ConfigContent { +): { data?: ConfigContent; error?: Error } { if (!isValidCreateConfig(config)) { - throw new Error("Config must have a provider field for creation"); + return { + error: new Error("Config must have a provider field for creation"), + }; } - return config; + return { data: config }; } export type { InstallationConfigContent }; diff --git a/src/headless/installation/useCreateInstallation.ts b/src/headless/installation/useCreateInstallation.ts index 34f58a7cb..0171c84f3 100644 --- a/src/headless/installation/useCreateInstallation.ts +++ b/src/headless/installation/useCreateInstallation.ts @@ -61,6 +61,15 @@ export function useCreateInstallation() { onSettled?.(); return; } + + // Validate config before creating installation + const configResult = toCreateConfigContent(config); + if (configResult.error || !configResult.data) { + onError?.(configResult.error || new Error("Invalid config")); + onSettled?.(); + return; + } + // assemble create installation requests from providers const createInstallationRequest: CreateInstallationOperationRequest = { projectIdOrName, @@ -69,7 +78,7 @@ export function useCreateInstallation() { groupRef, connectionId: connection?.id, config: { - content: toCreateConfigContent(config), + content: configResult.data, }, }, }; diff --git a/src/hooks/mutation/useUpdateOauthConnectMutation.ts b/src/hooks/mutation/useUpdateOauthConnectMutation.ts index 1bfa23263..5a5c9c3c2 100644 --- a/src/hooks/mutation/useUpdateOauthConnectMutation.ts +++ b/src/hooks/mutation/useUpdateOauthConnectMutation.ts @@ -8,10 +8,14 @@ export const useUpdateOauthConnectMutation = () => { return useMutation({ mutationKey: ["updateOauthConnection"], mutationFn: async (request: OauthUpdateRequest) => { - const api = await getAPI(); + // Validate required fields before making API call if (!request.projectIdOrName || !request.connectionId) { - throw new Error("Project ID and connection ID are required"); + // Return rejected promise instead of throw to be more explicit + return Promise.reject( + new Error("Project ID and connection ID are required"), + ); } + const api = await getAPI(); return api.oAuthApi.oauthUpdate(request); }, onSuccess: () => { From 5737b94c061b175f1f965ca1f0e437c5162f43f4 Mon Sep 17 00:00:00 2001 From: Dion Low Date: Tue, 21 Oct 2025 14:32:57 -0700 Subject: [PATCH 2/4] Update src/headless/installation/useCreateInstallation.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/headless/installation/useCreateInstallation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/headless/installation/useCreateInstallation.ts b/src/headless/installation/useCreateInstallation.ts index 0171c84f3..ced8e0dc6 100644 --- a/src/headless/installation/useCreateInstallation.ts +++ b/src/headless/installation/useCreateInstallation.ts @@ -64,8 +64,8 @@ export function useCreateInstallation() { // Validate config before creating installation const configResult = toCreateConfigContent(config); - if (configResult.error || !configResult.data) { - onError?.(configResult.error || new Error("Invalid config")); + if (configResult.error) { + onError?.(configResult.error); onSettled?.(); return; } From cc4bb7ff47a6791753a3830d8687c6b92b14cf95 Mon Sep 17 00:00:00 2001 From: Dion Low Date: Tue, 21 Oct 2025 14:37:49 -0700 Subject: [PATCH 3/4] style: apply linter formatting fixes --- src/headless/config/types.ts | 7 ++++--- src/headless/installation/useCreateInstallation.ts | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/headless/config/types.ts b/src/headless/config/types.ts index 73e08b4fb..71805867e 100644 --- a/src/headless/config/types.ts +++ b/src/headless/config/types.ts @@ -32,9 +32,10 @@ export function toUpdateConfigContent( * Helper to convert InstallationConfigContent to ConfigContent * Returns result object with either data or error */ -export function toCreateConfigContent( - config: InstallationConfigContent, -): { data?: ConfigContent; error?: Error } { +export function toCreateConfigContent(config: InstallationConfigContent): { + data?: ConfigContent; + error?: Error; +} { if (!isValidCreateConfig(config)) { return { error: new Error("Config must have a provider field for creation"), diff --git a/src/headless/installation/useCreateInstallation.ts b/src/headless/installation/useCreateInstallation.ts index ced8e0dc6..a52496b06 100644 --- a/src/headless/installation/useCreateInstallation.ts +++ b/src/headless/installation/useCreateInstallation.ts @@ -50,7 +50,9 @@ export function useCreateInstallation() { onSettled?: () => void; }) => { if (installation) { - const error = new Error("Installation already created. Try updating instead."); + const error = new Error( + "Installation already created. Try updating instead.", + ); onError?.(error); onSettled?.(); return; From 7a0311696561e48d1709ac74462a4d956493d359 Mon Sep 17 00:00:00 2001 From: Dion Low Date: Thu, 23 Oct 2025 12:21:21 -0700 Subject: [PATCH 4/4] fix build --- src/headless/installation/useCreateInstallation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/headless/installation/useCreateInstallation.ts b/src/headless/installation/useCreateInstallation.ts index a52496b06..14a2d76fc 100644 --- a/src/headless/installation/useCreateInstallation.ts +++ b/src/headless/installation/useCreateInstallation.ts @@ -66,8 +66,10 @@ export function useCreateInstallation() { // Validate config before creating installation const configResult = toCreateConfigContent(config); - if (configResult.error) { - onError?.(configResult.error); + if (configResult.error || !configResult.data) { + onError?.( + configResult.error || new Error("Invalid configuration data"), + ); onSettled?.(); return; }