From 1c1347a827d88908fe06f78cde737b1f434ef11b Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Wed, 13 Aug 2025 07:17:45 -0400 Subject: [PATCH 1/3] Fix feature discovery crash with packages using modern Node.js exports fields When using packages that employ modern Node.js "exports" fields without exporting ./package.json (such as OpenTelemetry packages), the feature discovery service crashes with ERR_PACKAGE_PATH_NOT_EXPORTED errors. This happens because these packages restrict which files can be accessed via require(), and many don't expose their package.json files. Wraps the package.json resolution with error handling to gracefully skip packages that don't export their package.json. Since these packages can't be Backstage plugins anyway (we need access to the backstage.role field). Signed-off-by: Brian Hudson --- .../src/PackageDiscoveryService.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/backend-defaults/src/PackageDiscoveryService.ts b/packages/backend-defaults/src/PackageDiscoveryService.ts index 90e8d0f59d..ef95bc9f5e 100644 --- a/packages/backend-defaults/src/PackageDiscoveryService.ts +++ b/packages/backend-defaults/src/PackageDiscoveryService.ts @@ -125,9 +125,22 @@ export class PackageDiscoveryService { const features: BackendFeature[] = []; for (const name of dependencyNames) { - const depPkg = require(require.resolve(`${name}/package.json`, { - paths: [packageDir], - })) as BackstagePackageJson; + let depPkg: BackstagePackageJson; + try { + const packageJsonPath = require.resolve(`${name}/package.json`, { + paths: [packageDir], + }); + depPkg = require(packageJsonPath) as BackstagePackageJson; + } catch (error) { + // Handle packages with "exports" field that don't export ./package.json + if ( + error instanceof Error && + (error as any).code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' + ) { + continue; // Skip packages that don't export package.json - they can't be Backstage packages + } + throw error; + } if ( !depPkg?.backstage?.role || !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role) From ed74af531d1a8f0ce21421b6062c5504ffce97e3 Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Wed, 13 Aug 2025 07:20:41 -0400 Subject: [PATCH 2/3] Add changeset Signed-off-by: Brian Hudson --- .changeset/lovely-cobras-doubt.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/lovely-cobras-doubt.md diff --git a/.changeset/lovely-cobras-doubt.md b/.changeset/lovely-cobras-doubt.md new file mode 100644 index 0000000000..765a412d8a --- /dev/null +++ b/.changeset/lovely-cobras-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Fixed bug in PackageDiscoveryService where packages with "exports" field caused ERR_PACKAGE_PATH_NOT_EXPORTED error during backend startup. From cd97cc8e0df4aa0a9067b33775eded669e7edb01 Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Wed, 13 Aug 2025 07:51:48 -0400 Subject: [PATCH 3/3] Use isError Signed-off-by: Brian Hudson --- packages/backend-defaults/src/PackageDiscoveryService.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/backend-defaults/src/PackageDiscoveryService.ts b/packages/backend-defaults/src/PackageDiscoveryService.ts index ef95bc9f5e..fe0ab931a7 100644 --- a/packages/backend-defaults/src/PackageDiscoveryService.ts +++ b/packages/backend-defaults/src/PackageDiscoveryService.ts @@ -23,6 +23,7 @@ import { RootLoggerService, } from '@backstage/backend-plugin-api'; import { BackstagePackageJson } from '@backstage/cli-node'; +import { isError } from '@backstage/errors'; const DETECTED_PACKAGE_ROLES = [ 'node-library', @@ -133,10 +134,7 @@ export class PackageDiscoveryService { depPkg = require(packageJsonPath) as BackstagePackageJson; } catch (error) { // Handle packages with "exports" field that don't export ./package.json - if ( - error instanceof Error && - (error as any).code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' - ) { + if (isError(error) && error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { continue; // Skip packages that don't export package.json - they can't be Backstage packages } throw error;