diff --git a/src/components/Configure/ErrorBoundary.tsx b/src/components/Configure/ErrorBoundary.tsx
new file mode 100644
index 000000000..4552003a6
--- /dev/null
+++ b/src/components/Configure/ErrorBoundary.tsx
@@ -0,0 +1,66 @@
+/* eslint-disable react/prop-types */
+import React, { Component, ReactNode } from "react";
+
+import { ComponentContainerError } from "./ComponentContainer";
+
+interface ErrorBoundaryProps {
+ children: ReactNode;
+ fallback?: ReactNode;
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
+}
+
+interface ErrorBoundaryState {
+ hasError: boolean;
+ error: Error | null;
+}
+
+/**
+ * React Error Boundary component that catches JavaScript errors anywhere in the child component tree
+ * and displays a fallback UI instead of crashing the whole application.
+ *
+ * This is especially important for the InstallIntegration component to prevent errors in hooks
+ * or rendering from crashing the parent application.
+ */
+export class InstallIntegrationErrorBoundary extends Component<
+ ErrorBoundaryProps,
+ ErrorBoundaryState
+> {
+ constructor(props: ErrorBoundaryProps) {
+ super(props);
+ this.state = { hasError: false, error: null };
+ }
+
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
+ // Update state so the next render will show the fallback UI
+ return { hasError: true, error };
+ }
+
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
+ // Log error to console for debugging
+ console.error("InstallIntegration Error Boundary caught an error:", error);
+ console.error("[Error Info]:", errorInfo);
+
+ // Call optional error callback
+ this.props.onError?.(error, errorInfo);
+ }
+
+ render(): ReactNode {
+ if (this.state.hasError) {
+ // Use custom fallback if provided, otherwise use default
+ if (this.props.fallback) {
+ return this.props.fallback;
+ }
+
+ return (
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
diff --git a/src/components/Configure/InstallIntegration.tsx b/src/components/Configure/InstallIntegration.tsx
index af51ce3b5..6e13fb13e 100644
--- a/src/components/Configure/InstallIntegration.tsx
+++ b/src/components/Configure/InstallIntegration.tsx
@@ -13,6 +13,7 @@ import {
ComponentContainerLoading,
} from "./ComponentContainer";
import { InstallationContent } from "./content/InstallationContent";
+import { InstallIntegrationErrorBoundary } from "./ErrorBoundary";
import { ConditionalHasConfigurationLayout } from "./layout/ConditionalHasConfigurationLayout/ConditionalHasConfigurationLayout";
import { ProtectedConnectionLayout } from "./layout/ProtectedConnectionLayout";
import { ObjectManagementNav } from "./nav/ObjectManagementNav";
@@ -165,15 +166,22 @@ export function InstallIntegration({
};
return (
- // eventually will use the headless providers for integration, consumer, and group etc
-
+ }
>
-
-
+ {/* eventually will use the headless providers for integration, consumer, and group etc */}
+
+
+
+
);
}
diff --git a/src/components/Configure/content/ConfigureInstallationBase.tsx b/src/components/Configure/content/ConfigureInstallationBase.tsx
index e637eb135..b63b72c67 100644
--- a/src/components/Configure/content/ConfigureInstallationBase.tsx
+++ b/src/components/Configure/content/ConfigureInstallationBase.tsx
@@ -127,9 +127,10 @@ export function ConfigureInstallationBase({
const isStateNew = isModified || isCreateMode || isSelectedReadObjectComplete;
// if the selected read object has an error in the manifest, it should not be saved
- const isSelectedReadObjectError = !!hydratedRevision?.content?.read?.objects?.find(
- (obj) => obj.objectName === selectedObjectName,
- )?.error;
+ const isSelectedReadObjectError =
+ !!hydratedRevision?.content?.read?.objects?.find(
+ (obj) => obj.objectName === selectedObjectName,
+ )?.error;
// should the save button be disabled?
const isDisabled =
diff --git a/src/components/Configure/index.ts b/src/components/Configure/index.ts
index ae00fa05c..05f1b8fad 100644
--- a/src/components/Configure/index.ts
+++ b/src/components/Configure/index.ts
@@ -1 +1,2 @@
export { InstallIntegration } from "./InstallIntegration";
+export { InstallIntegrationErrorBoundary } from "./ErrorBoundary";
diff --git a/src/headless/installation/useCreateInstallation.ts b/src/headless/installation/useCreateInstallation.ts
index 34f58a7cb..32f56ba3c 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;