Skip to content

Using duplex() with fd3: it must be a writable stream #1214

@matpen

Description

@matpen

I am trying to use execa within a pipeline, which results in very elegant code.

However, I am facing a problem when using an input file descriptor different from stdin. The following snippet contains a

minimal reproducer

// Importing dependencies
import fs from 'node:fs';
import dedent from 'dedent';
import { execa } from 'execa';
import { pipeline } from 'node:stream/promises';
import { Readable } from 'node:stream';

// Run execa as duplex stream in a pipeline
// The pipeline source and sink, as well as the subprocess (Python here) are not important,
// instead the focus is on the "to" option for duplex()
await pipeline(
    Readable.from('This is a test'),
    execa(
        'python3',
        {
            input: dedent(`
                import os
                with os.fdopen(3, 'r') as file:
                    content = json.load(file)
                print(content)
            `),
            stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
        },
    ).duplex({ to: 'fd3' }),
    process.stdout,
);

Running the code on GNU/Linux 6.14.0 with Node.js v22.15.0 and execa 9.6.0 causes the following

error message

file:///tmp/test/node_modules/execa/lib/arguments/fd-options.js:66
                throw new TypeError(`"${getOptionName(isWritable)}" must not be ${fdName}. It must be a writable stream, not readable.`);
                      ^

TypeError: "to" must not be fd3. It must be a writable stream, not readable.
    at validateFdNumber (file:///tmp/test/node_modules/execa/lib/arguments/fd-options.js:66:9)
    at getFdNumber (file:///tmp/test/node_modules/execa/lib/arguments/fd-options.js:36:2)
    at getToStream (file:///tmp/test/node_modules/execa/lib/arguments/fd-options.js:7:19)
    at getSubprocessStdin (file:///tmp/test/node_modules/execa/lib/convert/writable.js:32:26)
    at createDuplex (file:///tmp/test/node_modules/execa/lib/convert/duplex.js:22:68)
    at main (file:///tmp/test/[eval1]:29:11)
    at file:///tmp/test/[eval1]:37:2
    at ModuleJob.run (node:internal/modules/esm/module_job:274:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:268:26)
    at async ModuleLoader.executeModuleJob (node:internal/modules/esm/loader:264:20)

I tracked the source, and noticed that by simply removing the validation rules, the code runs and behaves as expected. Unfortunately, I am not familiar enough with the source code to suggest a solution, so I would appreciate any help by the maintainers or more experienced users.

Thank you for a wonderful library, and its extensive documentation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions