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

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions