Skip to content

fs.realpath on dangling symlink silently aborts node_modules scan, causing misleading "Failed to load remote context" errors #174

Description

@elf-pavlik

AI generated, possibly related to some issues reported earlier with "Failed to load remote contexts"

Issue type:

  • 🐛 Bug

Description:

ModuleStateBuilder.buildNodeModulePathsInner calls await fs.realpath(path) outside the try { … } catch { /* Ignore invalid paths */ } block, while the recursive scan of scoped packages inside that block runs under the parent's catch. In an npm workspace, deleting a workspace package leaves a dangling node_modules/@scope/pkg symlink. fs.realpath then throws ENOENT; the scope's Promise.all rejects; the parent's for…of loop over the top-level node_modules entries aborts; and the parent's catch silently swallows the failure.

The result is a silently truncated scan: every top-level package alphabetically after the dangling scope is never visited. Packages such as asynchronous-handlers therefore never get their lsd:contexts registered, and config builds fail with a misleading error claiming a missing/invalid dependency:

Failed to load remote context https://linkedsoftwaredependencies.org/bundles/npm/asynchronous-handlers/^1.0.0/components/context.jsonld: Detected remote context lookup for 'https://linkedsoftwaredependencies.org/bundles/npm/asynchronous-handlers/^1.0.0/components/context.jsonld' in …/node_modules/@solid/community-server/dist/authentication/CredentialsExtractor.jsonld. This may indicate a missing or invalid dependency, incorrect version number, or an invalid context URL.

Observed with vs. without the dangling symlink (same tree, same config):

state discovery log
with dangling node_modules/@janeirodigital/css-test-utils symlink Discovered 286 component packages within 517 packages → build fails
symlink removed Discovered 292 component packages within 1750 packages → build succeeds

Suggested fix: wrap the fs.realpath call in the same defensive handling as the rest of the method (catch ENOENT/skip the entry), and/or don't let a mid-scan failure abort and then get swallowed — e.g. Promise.allSettled, or log the skipped path.


Environment:

  • Components.js: 6.4.0
  • Node.js: v24.15.0
  • OS: Linux (also reproduced inside a Node 24 alpine container)

Crash log:

Could not build the config files from …/packages/css-storage-fixture/test/data.json
Error: Error while parsing file "…/node_modules/@solid/community-server/dist/authentication/CredentialsExtractor.jsonld": Failed to load remote context https://linkedsoftwaredependencies.org/bundles/npm/asynchronous-handlers/^1.0.0/components/context.jsonld: Detected remote context lookup for 'https://linkedsoftwaredependencies.org/bundles/npm/asynchronous-handlers/^1.0.0/components/context.jsonld' in …/node_modules/@solid/community-server/dist/authentication/CredentialsExtractor.jsonld. This may indicate a missing or invalid dependency, incorrect version number, or an invalid context URL.
    at RdfParser.addPathToError (…/node_modules/componentsjs/lib/rdf/RdfParser.js:86:16)
    at PassThrough.<anonymous> (…/node_modules/componentsjs/lib/rdf/RdfParser.js:59:38)
    at PassThrough.emit (node:events:521:24)
    at JsonLdParser.<anonymous> (…/node_modules/rdf-parse/lib/RdfParser.js:63:47)
    at JsonLdParser.emit (node:events:521:24)
    at emitErrorNT (…/node_modules/readable-stream/lib/internal/streams/destroy.js:126:8)
    at emitErrorCloseNT (…/node_modules/readable-stream/lib/internal/streams/destroy.js:98:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:90:21)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions