Repository: nodejs/node
Pull request: src,lib: introduce `util.getSystemErrorMessage(err)`

This patch adds a new utility function which provides human-readable string description of the given system error code.

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: doc/api/util.md
patch: @@ -463,6 +463,26 @@ fs.access('file/that/does/not/exist', (err) => {
 });
 ```
 
+## `util.getSystemErrorMessage(err)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `err` {number}
+* Returns: {string}
+
+Returns the string message for a numeric error code that comes from a Node.js
+API.
+The mapping between error codes and string messages is platform-dependent.
+
+```js
+fs.access('file/that/does/not/exist', (err) => {
+  const name = util.getSystemErrorMessage(err.errno);
+  console.error(name);  // no such file or directory
+});
+```
+
 ## `util.inherits(constructor, superConstructor)`
 
 <!-- YAML

filename: lib/internal/util.js
patch: @@ -386,6 +386,10 @@ function getCWDURL() {
   return cachedURL;
 }
 
+function getSystemErrorMessage(err) {
+  return lazyUv().getErrorMessage(err);
+}
+
 function getSystemErrorName(err) {
   const entry = uvErrmapGet(err);
   return entry ? entry[0] : `Unknown system error ${err}`;
@@ -880,6 +884,7 @@ module.exports = {
   getStructuredStack,
   getSystemErrorMap,
   getSystemErrorName,
+  getSystemErrorMessage,
   guessHandleType,
   isError,
   isUnderNodeModules,

filename: lib/util.js
patch: @@ -81,6 +81,7 @@ const {
   deprecate,
   getSystemErrorMap,
   getSystemErrorName: internalErrorName,
+  getSystemErrorMessage: internalErrorMessage,
   promisify,
   defineLazyProperties,
 } = require('internal/util');
@@ -269,6 +270,18 @@ function callbackify(original) {
   return callbackified;
 }
 
+/**
+ * @param {number} err
+ * @returns {string}
+ */
+function getSystemErrorMessage(err) {
+  validateNumber(err, 'err');
+  if (err >= 0 || !NumberIsSafeInteger(err)) {
+    throw new ERR_OUT_OF_RANGE('err', 'a negative integer', err);
+  }
+  return internalErrorMessage(err);
+}
+
 /**
  * @param {number} err
  * @returns {string}
@@ -343,6 +356,7 @@ module.exports = {
   getCallSite,
   getSystemErrorMap,
   getSystemErrorName,
+  getSystemErrorMessage,
   inherits,
   inspect,
   isArray: deprecate(ArrayIsArray,

filename: src/uv.cc
patch: @@ -59,6 +59,15 @@ using v8::ReadOnly;
 using v8::String;
 using v8::Value;
 
+void GetErrMessage(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  int err = args[0].As<v8::Int32>()->Value();
+  CHECK_LT(err, 0);
+  char message[50];
+  uv_strerror_r(err, message, sizeof(message));
+  args.GetReturnValue().Set(OneByteString(env->isolate(), message));
+}
+
 void ErrName(const FunctionCallbackInfo<Value>& args) {
   Environment* env = Environment::GetCurrent(args);
   if (env->options()->pending_deprecation && env->EmitErrNameWarning()) {
@@ -70,8 +79,7 @@ void ErrName(const FunctionCallbackInfo<Value>& args) {
         "DEP0119").IsNothing())
     return;
   }
-  int err;
-  if (!args[0]->Int32Value(env->context()).To(&err)) return;
+  int err = args[0].As<v8::Int32>()->Value();
   CHECK_LT(err, 0);
   char name[50];
   uv_err_name_r(err, name, sizeof(name));
@@ -128,11 +136,13 @@ void Initialize(Local<Object> target,
   }
 
   SetMethod(context, target, "getErrorMap", GetErrMap);
+  SetMethod(context, target, "getErrorMessage", GetErrMessage);
 }
 
 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
   registry->Register(ErrName);
   registry->Register(GetErrMap);
+  registry->Register(GetErrMessage);
 }
 }  // namespace uv
 }  // namespace node

filename: test/parallel/test-uv-errno.js
patch: @@ -5,6 +5,7 @@ const common = require('../common');
 const assert = require('assert');
 const {
   getSystemErrorName,
+  getSystemErrorMessage,
   _errnoException
 } = require('util');
 
@@ -13,6 +14,7 @@ const uv = internalBinding('uv');
 const keys = Object.keys(uv);
 
 assert.strictEqual(uv.errname(-111111), 'Unknown system error -111111');
+assert.strictEqual(uv.getErrorMessage(-111111), 'Unknown system error -111111');
 
 keys.forEach((key) => {
   if (!key.startsWith('UV_'))
@@ -21,6 +23,8 @@ keys.forEach((key) => {
   const err = _errnoException(uv[key], 'test');
   const name = uv.errname(uv[key]);
   assert.strictEqual(getSystemErrorName(uv[key]), name);
+  assert.notStrictEqual(getSystemErrorMessage(uv[key]),
+                        `Unknown system error ${key}`);
   assert.strictEqual(err.code, name);
   assert.strictEqual(err.code, getSystemErrorName(err.errno));
   assert.strictEqual(err.message, `test ${name}`);
@@ -53,3 +57,4 @@ function runTest(fn) {
 
 runTest(_errnoException);
 runTest(getSystemErrorName);
+runTest(getSystemErrorMessage);