os.userInfo()

Returns the effective user's username, uid, gid, shell and home directory from the operating system.

Signature
os.userInfo([options])
  • Added in Node.js v6.0.0

Parameters

ParameterTypeDescription
$optionsoptionalObjectOptions object.
$options.encodingoptionalstring= 'utf8'Character encoding used to interpret the resulting strings. If set to 'buffer', the username, shell and homedir values are Buffer instances instead of strings.

Return value

ObjectAn object with `username` (string), `uid` (number), `gid` (number), `shell` (string) and `homedir` (string). On Windows `uid` and `gid` are `-1` and `shell` is `null`. When `encoding` is `'buffer'`, `username`, `shell` and `homedir` are `Buffer` instances.

Errors

Throws a SystemError if the effective user has no username or homedir — typically because no passwd entry exists for the running uid.

os.userInfo() reads the effective user's entry from the system account database — on POSIX a subset of the passwd record, on Windows a partial equivalent where uid and gid are -1 and shell is null.

When to reach for it

When you need the account rather than the environment: printing who a CLI is running as, deciding whether you are root by testing uid === 0, choosing file ownership, or getting a home directory that $HOME cannot lie about. It is the authoritative counterpart to os.homedir(), which prefers environment variables.

What trips people up

It throws. In slim and distroless containers run with --user 1000:1000, or under an OpenShift-style random UID, there is often no passwd entry for that uid, and the call fails with a SystemError rather than returning partial data. Wrap it in try/catch anywhere it might run in a container. Note that os.homedir() keeps working in that situation because it reads $HOME.

The other surprise is that a real user name and shell do not imply a real login session — a daemon under systemd gets the same fields. And uid === 0 tells you the effective uid, not whether Linux capabilities have been dropped.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Print the effective account in a CLI's diagnostic output
  • Refuse to run as root by checking whether uid is 0
  • Get a home directory that is not affected by a modified $HOME

Frequently asked questions

Why does os.userInfo() throw ERR_SYSTEM_ERROR in Docker?

Because the container is running under a uid with no matching entry in /etc/passwd, which is common in distroless and scratch images started with --user. Wrap the call in try/catch and fall back to process.getuid() and the USER environment variable, or add a passwd entry for the uid to the image.

How do I check if a Node.js process is running as root?

Test os.userInfo().uid === 0 on POSIX, or use process.getuid() === 0 which does not need an account database. Guard the check on Windows, where uid is always -1 and the concept does not apply; check for an elevated token there instead.

Why do os.userInfo().homedir and os.homedir() disagree?

Because os.userInfo() reads the account's home from the operating system while os.homedir() prefers the $HOME or %USERPROFILE% environment variable. Under sudo, inside systemd units and in containers where HOME is set explicitly, the environment wins and the two values diverge.

More examples

Call it safely where a passwd entry may be missing

On a normal machine the passwd branch runs and source reads "passwd". In a distroless image started with --user 1000:1000 the call throws and the fallback returns the uid from process.getuid(), which works without any account database — and os.homedir() keeps answering either way because it reads $HOME.

Example · javascript
const os = require('node:os');

function whoAmI() {
  try {
    const u = os.userInfo();
    return { username: u.username, uid: u.uid, gid: u.gid, source: 'passwd' };
  } catch (err) {
    // Slim containers run with --user often have no /etc/passwd entry.
    return {
      username: process.env.USER ?? process.env.USERNAME ?? 'unknown',
      uid: typeof process.getuid === 'function' ? process.getuid() : -1,
      gid: typeof process.getgid === 'function' ? process.getgid() : -1,
      source: `fallback (${err.code ?? err.name})`
    };
  }
}

console.log(whoAmI());
console.log('homedir still available:', os.homedir().length > 0);

Refuse to run as root

The Windows guard matters: uid is always -1 there, so an unguarded uid === 0 check is meaningless rather than wrong. The escape hatch is the other half — installers and container entrypoints legitimately run as root, so a hard refusal with no override just gets patched out downstream.

Example · javascript
const os = require('node:os');

function rootCheck({ allowRoot = false } = {}) {
  if (os.platform() === 'win32') return 'skipped: uid is not meaningful on Windows';
  const { uid, username } = os.userInfo();
  if (uid === 0 && !allowRoot) {
    return `refusing to run as ${username} (uid 0) — re-run as a normal user or pass --allow-root`;
  }
  return `running as ${username} (uid ${uid})`;
}

console.log(rootCheck());
console.log(rootCheck({ allowRoot: true }));

Buffer encoding for non-UTF-8 account names

Only username, shell and homedir become Buffers; uid and gid stay numbers. The byte-versus-character comparison is why the option exists: POSIX names are byte strings, so an account created with a non-UTF-8 locale would be mangled by the default decoding and the Buffer form preserves it exactly.

Example · javascript
const os = require('node:os');

const asString = os.userInfo();
const asBuffer = os.userInfo({ encoding: 'buffer' });

console.log('username type (default):', typeof asString.username);
console.log('username is Buffer:', Buffer.isBuffer(asBuffer.username));
console.log('homedir is Buffer:', Buffer.isBuffer(asBuffer.homedir));
console.log('uid stays a number:', typeof asBuffer.uid);
console.log('round-trips to the same text:', asBuffer.username.toString('utf8') === asString.username);
console.log('raw bytes:', asBuffer.username.length, 'vs chars:', asString.username.length);

Discussion

  • Be the first to comment on this lesson.