Skip to content

Writing to socket in Node.js 19 passes null in callback #47229

Description

@armanbilge

Version

v19.8.1

Platform

Linux armanbilge-sandbox-g0l1nfjuvbe 5.15.0-47-generic #51-Ubuntu SMP Thu Aug 11 07:51:15 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

const net = require("net");

const server = net.createServer();

server.listen(
  "tmp.sock",
  function() {
    const socket = new net.Socket();
    socket.connect(
      "tmp.sock",
      function() {
        socket.write(
          "hello world",
          function(x) {
            console.log(x);
            socket.destroy();
            server.close();
          }
        );
      }
    )
  }
);

How often does it reproduce? Is there a required condition?

No response

What is the expected behavior? Why is that the expected behavior?

$ npx node@18 unix.js 
undefined

What do you see instead?

$ npx node@19 unix.js 
null

Additional information

No response

Activity

  1. changed the title [-]Writing to unix socket in Node.js 19 passes `null` in callback[/-] [+]Writing to socket in Node.js 19 passes `null` in callback[/+] on Mar 23, 2023
  2. armanbilge commented on Mar 23, 2023

    @armanbilge
    Author

    Clarification: this is not specific to unix sockets.

    const net = require("net");
    
    const server = net.createServer();
    
    server.listen(
      "localhost:8123",
      function() {
        const socket = new net.Socket();
        socket.connect(
          "localhost:8123",
          function() {
            socket.write(
              "hello world",
              function(x) {
                console.log(x);
                socket.destroy();
                server.close();
              }
            );
          }
        )
      }
    );
  3. mscdex commented on Mar 23, 2023

    @mscdex
    Contributor

    FWIW in general (not just node core) it's usually best to just do !err in error-first callbacks, as sometimes you'll encounter undefined and other times null (or maybe even false in less common instances), but the meaning is the same.

  4. added
    netIssues and PRs related to the net subsystem.
    on Mar 23, 2023
  5. bnoordhuis commented on Mar 23, 2023

    @bnoordhuis
    Member

    I don't think this counts as a regression because, to the best of my knowledge, the documentation doesn't claim anywhere that the err argument is always undefined in case of success, only that it's false-y. Node has never been super strict in that respect.

    If the change from undefined to null is a problem for you, then you're welcome to open a pull request to change it back.

  6. armanbilge commented on Mar 23, 2023

    @armanbilge
    Author

    Aha, there seems to have been an issue complaining about the opposite problem :)

    Which resulted in this PR:

    I'd understand if this was changed across the codebase for a consistent API/UX, but it seems like it was changed only for Writable :) It looks like this change broke other libraries and type definitions as well.

    If the change from undefined to null is a problem for you, then you're welcome to open a pull request to change it back.

    @bnoordhuis with this additional context, do you still feel like that? Thanks!

  7. bnoordhuis commented on Mar 24, 2023

    @bnoordhuis
    Member

    cc @targos - this needs your input

    My suggestion: revert that change and declare that err === undefined going forward, now and forever.

  8. targos commented on Mar 26, 2023

    @targos
    Member

    Why my input? I'm not familiar with this issue.

  9. bnoordhuis commented on Mar 26, 2023

    @bnoordhuis
    Member

    Sorry, I meant @lpinca.

  10. lpinca commented on Mar 26, 2023

    @lpinca
    Member

    I would personally not revert it. It was done for consistency (see #44290 (comment)) and in major release because it was known that it could break something. Anyway, I have no strong opinion. I'm fine either way.

  11. armanbilge commented on Mar 26, 2023

    @armanbilge
    Author

    It was done for consistency

    That's interesting, because it's completely counter to my own experience :) the comment says:

    In the last 5 years I have been using TypeScript with Node and explicitly typing everything and providing extensive error handling for all events.

    I've extensively wrapped the Node.js net, tls, fs, zlib, and other APIs in Scala.js (where undefined and null are handled differently) and this is the first time I've encountered a null error instead of an undefined one. Since this happens on the happy-path it's not an easy thing to miss.

    Other codepaths continue to use undefined e.g.

    const net = require("net")
    const server = net.createServer()
    server.listen()
    server.close(x => console.log(x))
    $ npx node@19 test.js 
    undefined
    
  12. mscdex commented on Mar 26, 2023

    @mscdex
    Contributor

    Other codepaths continue to use undefined e.g.

    const net = require("net")
    const server = net.createServer()
    server.listen()
    server.close(x => console.log(x))
    $ npx node@19 test.js 
    undefined
    

    The 'close' event (which is what the callback passed to server.close() is attached to) is never passed any arguments, so x should be expected to be undefined.

  13. armanbilge commented on Mar 26, 2023

    @armanbilge
    Author

    Thanks for the input. That's not how it's working currently :)

    const net = require("net")
    const server = net.createServer()
    server.close(x => console.log(typeof(x)))
    $ npx node@19 test.js 
    object
    
  14. lpinca commented on Mar 26, 2023

    @lpinca
    Member

    I've extensively wrapped the Node.js net, tls, fs, zlib, and other APIs in Scala.js (where undefined and null are handled differently) and this is the first time I've encountered a null error instead of an undefined one.

    Here are some examples:

    const fs = require('fs');
    
    fs.open('test.txt', 'w', function (err, fd) {
      console.log(err); // null
      fs.write(fd, 'foo', function (err) {
        console.log(err); // null
        fs.close(fd, function (err) {
          console.log(err); // null
        });
      });
    });
    const zlib = require('zlib');
    
    zlib.deflate(Buffer.from('aaaaaaaaaa'), function (err, buf) {
      console.log(err); // null
      zlib.inflate(buf, function (err) {
        console.log(err); // null
      });
    });

    For net and tls see #44290 (comment).

  15. armanbilge commented on Mar 26, 2023

    @armanbilge
    Author

    Thanks! I guess I never encountered those due to using the fs.promises and zlib stream.Transform APIs.

    Ok, so if null is the blessed way to indicate no error, then everywhere using undefined (e.g. server.close()) should be changed?

  16. lpinca commented on Mar 26, 2023

    @lpinca
    Member

    Ok, so if null is the blessed way to indicate no error, then everywhere using undefined (e.g. server.close()) should be changed?

    I think so. The server.close() case is weird because the callback is added as a listener for the 'close' event. The 'close' event is emitted without arguments so undefined is expected, but the callback is also called with an error if the server is not listening. I don't know if there are other cases like this.

  17. mk-pmb commented on Mar 26, 2023

    @mk-pmb

    The blessed way to deal with it, in cases where there really even is a potential error, is to apply the robustness principle. Usually you'll use that first argument in a condition expression anyway, either bare or negated. If you use it in a way where undefined and null have a meaningfull difference, I suspect there might be a bigger problem in your code.
    Anyone who really loves to have === true or === false in their condition can still explicitly cast to boolean.

  18. armanbilge commented on Mar 27, 2023

    @armanbilge
    Author

    IMHO the two paths from here are:

    1. Change them all to null (or change them all to undefined), for consistency and principle of least surprise; or

    2. Don't change anything anymore, because it's unnecessary breakage for non-robust codebases, and users correctly applying robustness won't be surprised either way.

    Personally I'm fine with (2) under the assumption that #44312 would be the last time this sort of change squeaks in 😁 but if these changes are going to keep happening, wouldn't it be better to decide on a consistent API and rip off the bandaid?

  19. everhardt commented on Mar 27, 2023

    @everhardt

    If you use it in a way where undefined and null have a meaningfull difference, I suspect there might be a bigger problem in your code.

    When you use Typescript (depending on your tsconfig settings) this is typically not true and you do have to check for both. And nowadays, one might suspect a bigger problem in your code when you're not using Typescript ;).

  20. jswalden commented on May 1, 2025

    @jswalden

    I kicked off the process of getting the TypeScript bindings changed to use err?: Error | null rather than err?: Error to align with reality here. DefinitelyTyped/DefinitelyTyped#72657

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    netIssues and PRs related to the net subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions