Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

child_process stdout being truncated #19218

Open
milesj opened this issue Mar 8, 2018 · 32 comments
Open

child_process stdout being truncated #19218

milesj opened this issue Mar 8, 2018 · 32 comments
Labels
child_process Issues and PRs related to the child_process subsystem. macos Issues and PRs related to the macOS platform / OSX. stdio Issues and PRs related to stdio.

Comments

@milesj
Copy link

milesj commented Mar 8, 2018

  • Version: 9.7.1
  • Platform: Darwin miless-mbp.lan 17.4.0 Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64 x86_64
  • Subsystem:

It seems like spawn() and exec() is truncating stdout, even when maxBuffer is set. I've been trying to capture the output of jest --help, which the full output looks like the following:

Usage: jest [--config=<pathToConfigFile>] [TestPathPattern]

Options:
  --help, -h                      Show help                            [boolean]
  --all                           The opposite of `onlyChanged`. If
                                  `onlyChanged` is set by default, running jest
                                  with `--all` will force Jest to run all tests
                                  instead of running only tests related to
                                  changed files.
  --automock                      Automock all files by default.       [boolean]
  --bail, -b                      Exit the test suite immediately upon the first
                                  failing test.                        [boolean]
  --browser                       Respect the "browser" field in package.json
                                  when resolving modules. Some packages export
                                  different versions based on whether they are
                                  operating in node.js or a browser.   [boolean]
  --cache                         Whether to use the transform cache. Disable
                                  the cache using --no-cache.          [boolean]
  --cacheDirectory                The directory where Jest should store its
                                  cached  dependency information.       [string]
  --changedFilesWithAncestor      Runs tests related to the current changes and
                                  the changes made in the last commit. Behaves
                                  similarly to `--onlyChanged`.        [boolean]
  --changedSince                  Runs tests related the changes since the
                                  provided branch. If the current branch has
                                  diverged from the given branch, then only
                                  changes made locally will be tested. Behaves
                                  similarly to `--onlyChanged`.         [string]
  --ci                            Whether to run Jest in continuous integration
                                  (CI) mode. This option is on by default in
                                  most popular CI environments. It will  prevent
                                  snapshots from being written unless explicitly
                                  requested.          [boolean] [default: false]
  --clearCache                    Clears the configured Jest cache directory and
                                  then exits. Default directory can be found by
                                  calling jest --showConfig            [boolean]
  --clearMocks                    Automatically clear mock calls and instances
                                  between every test. Equivalent to calling
                                  jest.clearAllMocks() between each test.
                                                                       [boolean]
  --collectCoverage               Alias for --coverage.                [boolean]
  --collectCoverageFrom           An array of glob patterns relative to
                                  <rootDir> matching the files that coverage
                                  info needs to be collected from.       [array]
  --collectCoverageOnlyFrom       Explicit list of paths coverage will be
                                  restricted to.                         [array]
  --color                         Forces test results output color highlighting
                                  (even if stdout is not a TTY). Set to false if
                                  you would like to have no colors.    [boolean]
  --colors                        Alias for `--color`.                 [boolean]
  --config, -c                    The path to a jest config file specifying how
                                  to find and execute tests. If no rootDir is
                                  set in the config, the current directory is
                                  assumed to be the rootDir for the project.
                                  This can also be a JSON encoded value which
                                  Jest will use as configuration.       [string]
  --coverage                      Indicates that test coverage information
                                  should be collected and reported in the
                                  output.                              [boolean]
  --coverageDirectory             The directory where Jest should output its
                                  coverage files.                       [string]
  --coveragePathIgnorePatterns    An array of regexp pattern strings that are
                                  matched against all file paths before
                                  executing the test. If the file pathmatches
                                  any of the patterns, coverage information will
                                  be skipped.                            [array]
  --coverageReporters             A list of reporter names that Jest uses when
                                  writing coverage reports. Any istanbul
                                  reporter can be used.                  [array]
  --coverageThreshold             A JSON string with which will be used to
                                  configure minimum threshold enforcement for
                                  coverage results                      [string]
  --debug                         Print debugging info about your jest config.
                                                                       [boolean]
  --detectLeaks                   **EXPERIMENTAL**: Detect memory leaks in
                                  tests. After executing a test, it will try to
                                  garbage collect the global object used, and
                                  fail if it was leaked
                                                      [boolean] [default: false]
  --env                           The test environment used for all tests. This
                                  can point to any file or node module.
                                  Examples: `jsdom`, `node` or
                                  `path/to/my-environment.js`           [string]
  --expand, -e                    Use this flag to show full diffs instead of a
                                  patch.                               [boolean]
  --findRelatedTests              Find related tests for a list of source files
                                  that were passed in as arguments. Useful for
                                  pre-commit hook integration to run the minimal
                                  amount of tests necessary.           [boolean]
  --forceExit                     Force Jest to exit after all tests have
                                  completed running. This is useful when
                                  resources set up by test code cannot be
                                  adequately cleaned up.               [boolean]
  --globalSetup                   The path to a module that runs before All
                                  Tests.                                [string]
  --globalTeardown                The path to a module that runs after All
                                  Tests.                                [string]
  --globals                       A JSON string with map of global variables
                                  that need to be available in all test
                                  environments.                         [string]
  --haste                         A JSON string with map of variables for the
                                  haste module system                   [string]
  --json                          Prints the test results in JSON. This mode
                                  will send all other test output and user
                                  messages to stderr.                  [boolean]
  --lastCommit                    Run all tests affected by file changes in the
                                  last commit made. Behaves similarly to
                                  `--onlyChanged`.                     [boolean]
  --listTests                     Lists all tests Jest will run given the
                                  arguments and exits. Most useful in a CI
                                  system together with `--findRelatedTests` to
                                  determine the tests Jest will run based on
                                  specific files      [boolean] [default: false]
  --logHeapUsage                  Logs the heap usage after every test. Useful
                                  to debug memory leaks. Use together with
                                  `--runInBand` and `--expose-gc` in node.
                                                                       [boolean]
  --mapCoverage                   Maps code coverage reports against original
                                  source code when transformers supply source
                                  maps.

                                  DEPRECATED                           [boolean]
  --maxWorkers, -w                Specifies the maximum number of workers the
                                  worker-pool will spawn for running tests. This
                                  defaults to the number of the cores available
                                  on your machine. (its usually best not to
                                  override this default)                [number]
  --moduleDirectories             An array of directory names to be searched
                                  recursively up from the requiring module's
                                  location.                              [array]
  --moduleFileExtensions          An array of file extensions your modules use.
                                  If you require modules without specifying a
                                  file extension, these are the extensions Jest
                                  will look for.                         [array]
  --moduleNameMapper              A JSON string with a map from regular
                                  expressions to module names that allow to stub
                                  out resources, like images or styles with a
                                  single module                         [string]
  --modulePathIgnorePatterns      An array of regexp pattern strings that are
                                  matched against all module paths before those
                                  paths are to be considered "visible" to the
                                  module loader.                         [array]
  --modulePaths                   An alternative API to setting the NODE_PATH
                                  env variable, modulePaths is an array of
                                  absolute paths to additional locations to
                                  search when resolving modules.         [array]
  --noStackTrace                  Disables stack trace in test results output
                                                                       [boolean]
  --notify                        Activates notifications for test results.
                                                                       [boolean]
  --notifyMode                    Specifies when notifications will appear for
                                  test results.     [string] [default: "always"]
  --onlyChanged, -o               Attempts to identify which tests to run based
                                  on which files have changed in the current
                                  repository. Only works if you're running tests
                                  in a git or hg repository at the moment.
                                                                       [boolean]
  --onlyFailures, -f              Run tests that failed in the previous
                                  execution.                           [boolean]
  --outputFile                    Write test results to a file when the --json
                                  option is also specified.             [string]
  --passWithNoTests               Will not fail if no tests are found (for
                                  example while using `--testPathPattern`.)
                                                      [boolean] [default: false]
  --preset                        A preset that is used as a base for Jest's
                                  configuration.                        [string]
  --projects                      A list of projects that use Jest to run all
                                  tests of all projects in a single instance of
                                  Jest.                                  [array]
  --reporters                     A list of custom reporters for the test suite.
                                                                         [array]
  --resetMocks                    Automatically reset mock state between every
                                  test. Equivalent to calling
                                  jest.resetAllMocks() between each test.
                                                                       [boolean]
  --resetModules                  If enabled, the module registry for every test
                                  file will be reset before running each
                                  individual test.                     [boolean]
  --resolver                      A JSON string which allows the use of a custom
                                  resolver.                             [string]
  --restoreMocks                  Automatically restore mock state and
                                  implementation between every test. Equivalent
                                  to calling jest.restoreAllMocks() between each
                                  test.                                [boolean]
  --rootDir                       The root directory that Jest should scan for
                                  tests and modules within.             [string]
  --roots                         A list of paths to directories that Jest
                                  should use to search for files in.     [array]
  --runInBand, -i                 Run all tests serially in the current process
                                  (rather than creating a worker pool of child
                                  processes that run tests). This is sometimes
                                  useful for debugging, but such use cases are
                                  pretty rare.                         [boolean]
  --runTestsByPath                Used when provided patterns are exact file
                                  paths. This avoids converting them into a
                                  regular expression and matching it against
                                  every single file.  [boolean] [default: false]
  --setupFiles                    The paths to modules that run some code to
                                  configure or set up the testing environment
                                  before each test.                      [array]
  --setupTestFrameworkScriptFile  The path to a module that runs some code to
                                  configure or set up the testing framework
                                  before each test.                     [string]
  --showConfig                    Print your jest config and then exits.
                                                                       [boolean]
  --silent                        Prevent tests from printing messages through
                                  the console.                         [boolean]
  --snapshotSerializers           A list of paths to snapshot serializer modules
                                  Jest should use for snapshot testing.  [array]
  --testEnvironment               Alias for --env                       [string]
  --testFailureExitCode           Exit code of `jest` command if the test run
                                  failed                                [string]
  --testLocationInResults         Add `location` information to the test results
                                                      [boolean] [default: false]
  --testMatch                     The glob patterns Jest uses to detect test
                                  files.                                 [array]
  --testNamePattern, -t           Run only tests with a name that matches the
                                  regex pattern.                        [string]
  --testPathIgnorePatterns        An array of regexp pattern strings that are
                                  matched against all test paths before
                                  executing the test. If the test path matches
                                  any of the patterns, it will be skipped.
                                                                         [array]
  --testPathPattern               A regexp pattern string that is matched
                                  against all tests paths before executing the
                                  test.                                  [array]
  --testRegex                     The regexp pattern Jest uses to detect test
                                  files.                                [string]
  --testResultsProcessor          Allows the use of a custom results processor.
                                  This processor must be a node module that
                                  exports a function expecting as the first
                                  argument the result object            [string]
  --testRunner                    Allows to specify a custom test runner. The
                                  default is  `jasmine2`. A path to a custom
                                  test runner can be provided:
                                  `<rootDir>/path/to/testRunner.js`.    [string]
  --testURL                       This option sets the URL for the jsdom
                                  environment.                          [string]
  --timers                        Setting this value to fake allows the use of
                                  fake timers for functions such as setTimeout.
                                                                        [string]
  --transform                     A JSON string which maps from regular
                                  expressions to paths to transformers. [string]
  --transformIgnorePatterns       An array of regexp pattern strings that are
                                  matched against all source file paths before
                                  transformation.                        [array]
  --unmockedModulePathPatterns    An array of regexp pattern strings that are
                                  matched against all modules before the module
                                  loader will automatically return a mock for
                                  them.                                  [array]
  --updateSnapshot, -u            Use this flag to re-record snapshots. Can be
                                  used together with a test suite pattern or
                                  with `--testNamePattern` to re-record snapshot
                                  for test matching the pattern        [boolean]
  --useStderr                     Divert all output to stderr.         [boolean]
  --verbose                       Display individual test results with the test
                                  suite hierarchy.                     [boolean]
  --watch                         Watch files for changes and rerun tests
                                  related to changed files. If you want to
                                  re-run all tests when a file has changed, use
                                  the `--watchAll` option.             [boolean]
  --watchAll                      Watch files for changes and rerun all tests.
                                  If you want to re-run only the tests related
                                  to the changed files, use the `--watch`
                                  option.                              [boolean]
  --watchPathIgnorePatterns       An array of regexp pattern strings that are
                                  matched against all paths before trigger test
                                  re-run in watch mode. If the test path matches
                                  any of the patterns, it will be skipped.
                                                                         [array]
  --watchman                      Whether to use watchman for file crawling.
                                  Disable using --no-watchman.         [boolean]

Documentation: https://facebook.github.io/jest/

spawn()

Here's an example spawn() script:

const cp = require('child_process');
const out = [];

const p = cp.spawn('./node_modules/.bin/jest', ['--help'], {
  cwd: process.cwd(),
  maxBuffer: 1000 * 1000 * 10, // 10 MB
});

p.stdout.on('data', line => {
  out.push(String(line));
});

p.on('close', code => {
  console.log(code);
  console.log(out.join(''));
});

And the captured output:

0
Usage: jest [--config=<pathToConfigFile>] [TestPathPattern]

Options:
  --help, -h                      Show help                            [boolean]
  --all                           The opposite of `onlyChanged`. If
                                  `onlyChanged` is set by default, running jest
                                  with `--all` will force Jest to run all tests
                                  instead of running only tests related to
                                  changed files.
  --automock                      Automock all files by default.       [boolean]
  --bail, -b                      Exit the test suite immediately upon the first
                                  failing test.                        [boolean]
  --browser                       Respect the "browser" field in package.json
                                  when resolving modules. Some packages export
                                  different versions based on whether they are
                                  operating in node.js or a browser.   [boolean]
  --cache                         Whether to use the transform cache. Disable
                                  the cache using --no-cache.          [boolean]
  --cacheDirectory                The directory where Jest should store its
                                  cached  dependency information.       [string]
  --changedFilesWithAncestor      Runs tests related to the current changes and
                                  the changes made in the last commit. Behaves
                                  similarly to `--onlyChanged`.        [boolean]
  --changedSince                  Runs tests related the changes since the
                                  provided branch. If the current branch has
                                  diverged from the given branch, then only
                                  changes made locally will be tested. Behaves
                                  similarly to `--onlyChanged`.         [string]
  --ci                            Whether to run Jest in continuous integration
                                  (CI) mode. This option is on by default in
                                  most popular CI environments. It will  prevent
                                  snapshots from being written unless explicitly
                                  requested.          [boolean] [default: false]
  --clearCache                    Clears the configured Jest cache directory and
                                  then exits. Default directory can be found by
                                  calling jest --showConfig            [boolean]
  --clearMocks                    Automatically clear mock calls and instances
                                  between every test. Equivalent to calling
                                  jest.clearAllMocks() between each test.
                                                                       [boolean]
  --collectCoverage               Alias for --coverage.                [boolean]
  --collectCoverageFrom           An array of glob patterns relative to
                                  <rootDir> matching the files that coverage
                                  info needs to be collected from.       [array]
  --collectCoverageOnlyFrom       Explicit list of paths coverage will be
                                  restricted to.                         [array]
  --color                         Forces test results output color highlighting
                                  (even if stdout is not a TTY). Set to false if
                                  you would like to have no colors.    [boolean]
  --colors                        Alias for `--color`.                 [boolean]
  --config, -c                    The path to a jest config file specifying how
                                  to find and execute tests. If no rootDir is
                                  set in the config, the current directory is
                                  assumed to be the rootDir for the project.
                                  This can also be a JSON encoded value which
                                  Jest will use as configuration.       [string]
  --coverage                      Indicates that test coverage information
                                  should be collected and reported in the
                                  output.                              [boolean]
  --coverageDirectory             The directory where Jest should output its
                                  coverage files.                       [string]
  --coveragePathIgnorePatterns    An array of regexp pattern strings that are
                                  matched against all file paths before
                                  executing the test. If the file pathmatches
                                  any of the patterns, coverage information will
                                  be skipped.                            [array]
  --coverageReporters             A list of reporter names that Jest uses when
                                  writing coverage reports. Any istanbul
                                  reporter can be used.                  [array]
  --coverageThreshold             A JSON string with which will be used to
                                  configure minimum threshold enforcement for
                                  coverage results                      [string]
  --debug                         Print debugging info about your jest config.
                                                                       [boolean]
  --detectLeaks                   **EXPERIMENTAL**: Detect memory leaks in
                                  tests. After executing a test, it will try to
                                  garbage collect the global object used, and
                                  fail if it was leaked
                                                      [boolean] [default: false]
  --env                           The test environment used for all tests. This
                                  can point to any file or node module.
                                  Examples: `jsdom`, `node` or
                                  `path/to/my-environment.js`           [string]
  --expand, -e                    Use this flag to show full diffs instead of a
                                  patch.                               [boolean]
  --findRelatedTests              Find related tests for a list of source files
                                  that were passed in as arguments. Useful for
                                  pre-commit hook integration to run the minimal
                                  amount of tests necessary.           [boolean]
  --forceExit                     Force Jest to exit after all tests have
                                  completed running. This is useful when
                                  resources set up by test code cannot be
                                  adequately cleaned up.               [boolean]
  --globalSetup                   The path to a module that runs before All
                                  Tests.                                [string]
  --globalTeardown                The path to a module that runs after All
                                  Tests.                                [string]
  --globals                       A JSON string with map of global variables
                                  that need to be available in all test
                                  environments.                         [string]
  --haste                         A JSON string with map of variables for the
                                  haste module system                   [string]
  --json                          Prints the test results in JSON. This mode
                                  will send all other test output and user
                                  messages to stderr.                  [boolean]
  --lastCommit                    Run all tests affected by file changes in the
                                  last commit made. Behaves similarly to

Notice that about half the output is missing.

exec()

Another attempt using exec().

const cp = require('child_process');

cp.exec('./node_modules/.bin/jest --help', {
  cwd: process.cwd(),
  maxBuffer: 1000 * 1000 * 10, // 10 MB
}, (error, stdout) => {
  console.log(stdout);
});

The output is the same as the truncated above.

Execa

Another attempt using execa.

const execa = require('execa');

execa('jest', ['--help']).then(result => {
  console.log(result.code);
  console.log(result.stdout);
});

The output is the same as the truncated above.

@addaleax addaleax added child_process Issues and PRs related to the child_process subsystem. macos Issues and PRs related to the macOS platform / OSX. labels Mar 8, 2018
@addaleax
Copy link
Member

addaleax commented Mar 8, 2018

The output is the same as above.

I assume, you mean the output is the truncated one?

even when maxBuffer is set

I think spawn ignores that anyway, so it’s probably not related.

Do you get the same issue with other scripts, e.g. when trying to run yes | head -n 10000 via exec()?

@gireeshpunathil
Copy link
Member

duplicate of #2360 or #2049?

@addaleax addaleax added confirmed-bug Issues with confirmed bugs. macos Issues and PRs related to the macOS platform / OSX. and removed macos Issues and PRs related to the macOS platform / OSX. confirmed-bug Issues with confirmed bugs. labels Mar 8, 2018
@milesj
Copy link
Author

milesj commented Mar 8, 2018

@addaleax Yes, the same output as the truncated one. I'll update the post.

I'll try with that script and report back.

@santigimeno
Copy link
Member

It looks a duplicate of #784. Making the stdio pipes blocking as suggested here fixes the issue though probably there was a reason why it didn't land.

@addaleax
Copy link
Member

addaleax commented Mar 8, 2018

@santigimeno That this also occurs with non-Node child processes makes that sound unlikely (assuming that that’s correct)?

@santigimeno
Copy link
Member

@addaleax I just tested with the jest command that is a node script afaik.

@addaleax
Copy link
Member

addaleax commented Mar 8, 2018

@santigimeno I asked the OP to also check with yes | head -n 10000 to figure out whether this is related to child process stdio; that they said it doesn’t stop the issue from appearing seems to indicate it is not?

@santigimeno
Copy link
Member

I think his response was he'll try and report back

@santigimeno
Copy link
Member

BTW, yes | head -n 10000 works correctly

@milesj
Copy link
Author

milesj commented Mar 8, 2018

@santigimeno Thanks for testing that for me.

@milesj
Copy link
Author

milesj commented Mar 11, 2018

I've tried blocking the process, but that doesn't seem to work either. Same truncated output.

Code as follows:

const cp = require('child_process');
const out = [];

const p = cp.spawn('./node_modules/.bin/jest', ['--help'], {
  cwd: process.cwd(),
  maxBuffer: 1000 * 1000 * 10, // 10 MB
});

p.stdout._handle.setBlocking(true);

p.stdout.on('data', line => {
  out.push(String(line));
});

p.on('close', code => {
  console.log(code);
  console.log(out.join(''));
});

@gireeshpunathil
Copy link
Member

This is easily reproducible in MAC through:
#cat 19218.js

const cp = require('child_process')
const op = {maxBuffer: 100 * 1024 * 1024}
if (process.argv[2]) {
  child = cp.spawn('node', ['-e', 'process.stdout.write(Buffer.alloc(10 * 1024 * 1024))'], op)
}
else
  child = cp.spawn('node', ['-e', 'console.log(Buffer.alloc(10 * 1024 * 1024))'], op)

let buf = ''
child.stdout.on('data', (c) => {
  buf += c
})

child.stdout.on('end', () => {
  console.log('ENDED')
  console.log(buf.length)
})

#node 19218.js

ENDED
164

#node 19218.js true

ENDED
10485760

maxBuffer is not a consideration in this problem.

And the root cause identified as the fact that console.log non blocking, so if a process issues it and exits afterwards, the actual number of bytes written depends on the time gap between the two actions and the capability of the I/O channel to transfer the data in between the gap.

I don't know the latest on this, also don't know the best practice / workaround. So /cc @nodejs/process @nodejs/child_process

@santigimeno
Copy link
Member

I've tried blocking the process, but that doesn't seem to work either. Same truncated output.

I think that's not working because you are making blocking the parent side of the pipe. For this to work the child process would need to make stdout/stderr blocking on its side (That's the reason why applying #784 (comment) to node would fix this issue)

@killagu
Copy link
Contributor

killagu commented Jun 13, 2018

@gireeshpunathil Your scripts can not reproduce the bug.

It should use child = cp.spawn('node', ['-e', 'console.log(Buffer.alloc(10 * 1024 * 1024).toString())'], op).

console.log(Buffer.alloc(0)) will print <Buffer >.

@gireeshpunathil
Copy link
Member

@killagu - thanks for checking. So basically the difference between yours and mine is that you add a toString() to get the actual data, while I didn't do that instead counted the numbers. The bug is not the way it is printed, but the fact that data was truncated right?

Nevertheless, no need to investigate this further as the underlying bug is well understood, and we have open issues on the same. The effort be better directed towards looking at how do we fix it. Does that sound good to you?

@killagu
Copy link
Contributor

killagu commented Jun 13, 2018

@gireeshpunathil If you use toString, It will print

ENDED
10485761

The data is not truncated.

If you do not use toString, it will print <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >, so the number is 164.

So the script can not reproduce the bug.

@gireeshpunathil
Copy link
Member

, so the number is 164.

can you please expand on that? what that number 164 signifies, according to you?

@gireeshpunathil
Copy link
Member

never mind, I got what you are saying. So the stringified version of the buffer object was passed across rather than the whole content. thanks for clarifying. How about this version?

const cp = require('child_process')
const op = {maxBuffer: 100 * 1024 * 1024}
if (process.argv[2] === 'child') {
  const buf = Buffer.alloc(10).toString()
  for(var i=0; i< 1024 * 1024; i++)
    console.log(buf)
  process.exit(0)
} else {

const child = cp.spawn(process.execPath, [__filename, 'child'])
let buf = ''
child.stdout.on('data', (c) => {
  buf += c
})

child.stdout.on('end', () => {
  console.log(buf.length)
})
}

shows random length if you run multiple times - both on linux and mac.

@killagu
Copy link
Contributor

killagu commented Jun 13, 2018

If you use toString, It will print

ENDED
10485761

Not use toString, it will print

ENDED
164

You can run the script @gireeshpunathil

@killagu
Copy link
Contributor

killagu commented Jun 13, 2018

@gireeshpunathil The new script works well. Thanks for the reply.

@benjie
Copy link

benjie commented Oct 31, 2018

I can reproduce this too (Node v10.11.0, v10.13.0, v11.0.0), the child.stdout._handle.setBlocking(true) workaround doesn't work for me.

const cp = require("child_process");
const op = { maxBuffer: 100 * 1024 * 1024 };
if (process.argv[2] === "child") {
  const buf = Buffer.alloc(10).toString();
  for (var i = 0; i < 1024 * 1024; i++) console.log(buf);
  process.exit(0);
} else {
  const child = cp.spawn(process.execPath, [__filename, "child"]);

  child.stdout._handle.setBlocking(true);

  let buf = "";
  child.stdout.on("data", c => {
    buf += c;
  });
  child.stdout.on("end", () => {
    console.log(buf.length);
  });
}
$ node test.js
1235597
$ node test.js
1214246
$ node test.js
1214257
$ node test.js
1159708
$ node test.js
1152624
$ node test.js
1150754

Is there another workaround at the moment?

@sam-github
Copy link
Contributor

Remove the process.exit() and let node exit when its flushed it's I/O.

@masaeedu
Copy link

@sam-github Is there a way to do this and still be able to dictate the exit code when Node eventually exits?

@benjie
Copy link

benjie commented Nov 19, 2018

You can do process.exitCode = 3 or whatever 👍

https://nodejs.org/api/process.html#process_process_exitcode

@Pointotech
Copy link

This problem still exists in child_process and in almost all of the libraries that build on it or that attempt to replace it. There's one library that just works and doesn't, in my experience, truncate the output: https://www.npmjs.com/package/cross-spawn

The maintainers of child_process might want to look at that library for solutions to this bug.

@aleksey-hoffman
Copy link

It looks like maxBuffer option isn't working at all.

In the example below, if you limit the output to 10 bytes, it will still show the whole 200 kb output:

const out = []

const p = childProcess.spawn('curl', ['https://stackoverflow.com'], {maxBuffer: 10})

p.stdout.on('data', line => {
  out.push(String(line))
})

p.on('close', () => {
  console.log(out.join(''))
})

Env:

Version: 15.14.0
Platform: Windows 10

@benjie
Copy link

benjie commented May 10, 2021

@aleksey-hoffman
Copy link

@benjie I see, only sync version supports it. Thanks.
So the problem with truncated output is not caused by the maxBuffer then

@akashkashyap
Copy link

akashkashyap commented Dec 2, 2021

I'm still seeing this problem when using exec and spawn on macOS Monterey 12.0.1. Is there a status on the fix?

@Dan-Wuensch
Copy link

Having this problem reading stdout on a large command output using exec() in macOS Monterey

@segevfiner
Copy link
Contributor

Still happens on macOS 14.4 and Node v18.19.1. This is a really serious bug in a very basic part of Node that I'm quite surprised didn't receive any attention... 😢

@segevfiner
Copy link
Contributor

Though in my case there isn't any process.exit it happens in the middle of the output, some of it is just dropped, and then resuming later on, my parent code doesn't have child.stdout._handle.setBlocking(true), I'm not sure why would output be dropped like that...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
child_process Issues and PRs related to the child_process subsystem. macos Issues and PRs related to the macOS platform / OSX. stdio Issues and PRs related to stdio.
Projects
None yet
Development

No branches or pull requests