Commit cfa1c0a76d5 for woocommerce

commit cfa1c0a76d5c310f1c974100a13e1f5bd6e7bd7a
Author: Copilot <198982749+Copilot@users.noreply.github.com>
Date:   Thu Jun 4 16:16:02 2026 -0500

    Blocks: Fix JS crash in useCollection when store error is not an Error instance (#64218)

    * Initial plan

    * Fix useCollection to handle non-Error objects from store gracefully

    Agent-Logs-Url: https://github.com/woocommerce/woocommerce/sessions/b5891181-0739-4b34-89e4-08d3ccdbcd7f

    Co-authored-by: kraftbj <88897+kraftbj@users.noreply.github.com>

    * Fix fallback message handling in useCollection

    * fix: preserve error metadata when wrapping non-Error store errors

    * refactor: extract wrapNonError helper and improve fallback context

    * fix: deduplicate non-Error log across useSelect double-invocation

    * refactor: simplify non-Error handling to inline message-or-fallback

    Per review feedback: store errors normally carry a message, so drop the
    wrapNonError helper, metadata preservation, and dedup. Inline the non-Error
    branch to throw a real Error with the original message or a generic fallback.

    ---------

    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: kraftbj <88897+kraftbj@users.noreply.github.com>
    Co-authored-by: Brandon Kraft <public@brandonkraft.com>

diff --git a/plugins/woocommerce/changelog/fix-blocks-use-collection-non-error-handling b/plugins/woocommerce/changelog/fix-blocks-use-collection-non-error-handling
new file mode 100644
index 00000000000..7ed2ff9c4d6
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-blocks-use-collection-non-error-handling
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Blocks: Fix JS crash when the store returns a non-Error value in useCollection; convert it to a proper Error instance so the error boundary handles it gracefully.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx
index 3bc3bd7ab97..f325d91df77 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx
@@ -328,4 +328,56 @@ describe( 'useCollection', () => {
 			[ 10, 30 ],
 		] );
 	} );
+	const renderWithStoreError = ( errorValue ) => {
+		mocks.selectors.getCollectionError.mockReturnValue( errorValue );
+		const TestComponent = getTestComponent();
+		act( () => {
+			renderer = TestRenderer.create(
+				getWrappedComponents( TestComponent, {
+					options: {
+						namespace: 'test/store',
+						resourceName: 'products',
+						query: { bar: 'foo' },
+					},
+				} )
+			);
+		} );
+		//eslint-disable-next-line testing-library/await-async-query
+		return renderer.root.findByType( 'div' ).props;
+	};
+
+	it( 'should propagate an Error instance from the store via the error boundary', () => {
+		const error = new Error( 'A real error' );
+		const props = renderWithStoreError( error );
+		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
+		expect( props[ 'data-error' ].message ).toBe( 'A real error' );
+		expect( console ).toHaveErrored( /your React components:/ );
+		renderer.unmount();
+	} );
+	it( 'should convert a non-Error object with a message into an Error instance', () => {
+		const error = { code: 'rest_no_route', message: 'No route found.' };
+		const props = renderWithStoreError( error );
+		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
+		expect( props[ 'data-error' ].message ).toBe( 'No route found.' );
+		expect( console ).toHaveErrored( /your React components:/ );
+		renderer.unmount();
+	} );
+	it( 'should use a fallback message when a non-Error object has no message', () => {
+		const props = renderWithStoreError( { code: 500 } );
+		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
+		expect( props[ 'data-error' ].message ).toBe(
+			'Something went wrong while loading data.'
+		);
+		expect( console ).toHaveErrored( /your React components:/ );
+		renderer.unmount();
+	} );
+	it( 'should use a fallback message when a primitive value is returned from the store', () => {
+		const props = renderWithStoreError( 'oops' );
+		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
+		expect( props[ 'data-error' ].message ).toBe(
+			'Something went wrong while loading data.'
+		);
+		expect( console ).toHaveErrored( /your React components:/ );
+		renderer.unmount();
+	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/use-collection.ts b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/use-collection.ts
index 161a465c741..d2a781e6e8d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/use-collection.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/use-collection.ts
@@ -6,6 +6,7 @@ import { useSelect } from '@wordpress/data';
 import { useRef } from '@wordpress/element';
 import { useShallowEqual, useThrowError } from '@woocommerce/base-hooks';
 import { isError } from '@woocommerce/types';
+import { __ } from '@wordpress/i18n';

 /**
  * This is a custom hook that is wired up to the `wc/store/collections` data
@@ -95,9 +96,20 @@ export const useCollection = < T >(
 				if ( isError( error ) ) {
 					throwError( error );
 				} else {
-					throw new Error(
-						'TypeError: `error` object is not an instance of Error constructor'
-					);
+					// Store errors (e.g. from the Store API) normally carry a
+					// message, but guard against non-Error values that don't so
+					// the error boundary always receives a real Error instance.
+					const message =
+						typeof error === 'object' &&
+						error !== null &&
+						typeof ( error as { message?: unknown } ).message ===
+							'string'
+							? ( error as { message: string } ).message
+							: __(
+									'Something went wrong while loading data.',
+									'woocommerce'
+							  );
+					throwError( new Error( message ) );
 				}
 			}