Skip to content

fix: enabled returns undefined instead of false when DEBUG env var is absent#1039

Open
JSap0914 wants to merge 1 commit into
debug-js:masterfrom
JSap0914:fix/enabled-returns-undefined-before-enable-call
Open

fix: enabled returns undefined instead of false when DEBUG env var is absent#1039
JSap0914 wants to merge 1 commit into
debug-js:masterfrom
JSap0914:fix/enabled-returns-undefined-before-enable-call

Conversation

@JSap0914

Copy link
Copy Markdown

Problem

When DEBUG is not set (or enable() is called with a non-string value such as undefined), instance.enabled incorrectly returns undefined instead of false.

Root cause

Inside enable(), createDebug.namespaces is assigned the raw argument:

createDebug.namespaces = namespaces; // can be undefined

Each debug instance caches its enabled state with:

let namespacesCache;   // initialised to undefined
let enabledCache;      // initialised to undefined

get: () => {
  if (namespacesCache !== createDebug.namespaces) { // undefined !== undefined → false!
    namespacesCache = createDebug.namespaces;
    enabledCache = createDebug.enabled(namespace);  // never reached on first access
  }
  return enabledCache; // returns undefined
}

Because both namespacesCache and createDebug.namespaces start as undefined, the cache-miss guard evaluates to false on the very first access, createDebug.enabled() is never called, and enabledCache remains undefined.

Observable effect

// No DEBUG env var set
const debug = require('debug');
const log = debug('myapp');

log.enabled              // → undefined  (expected: false)
log.enabled === false    // → false       (expected: true)
typeof log.enabled       // → 'undefined' (expected: 'boolean')

The common guard if (!debug.enabled) return; still works because !undefined === true, but strict comparisons and TypeScript type-checks fail.

Fix

Normalise createDebug.namespaces to an empty string when enable() receives a non-string argument, consistent with how the split step already handles non-string input:

createDebug.namespaces = typeof namespaces === 'string' ? namespaces : '';

This ensures the cache-miss guard fires on the first read after startup, so enabledCache is properly initialised.

Test

A new failing test is added that re-requires debug with no DEBUG env var and asserts typeof log.enabled === 'boolean' and log.enabled === false.

AI-assisted contribution.

When enable() is called with a non-string argument (including undefined,
which happens at startup when process.env.DEBUG is absent), it assigns
createDebug.namespaces = undefined.  The per-instance enabled getter
caches results using namespacesCache, which is initialised to undefined
in the closure.  The cache-miss guard

  if (namespacesCache !== createDebug.namespaces)

therefore evaluates to false on the very first access (undefined !==
undefined is false), so createDebug.enabled(namespace) is never invoked,
and enabledCache stays undefined.  Every subsequent read of instance.enabled
returns undefined instead of false until a second, string-valued enable()
call is made.

Fix: normalise createDebug.namespaces to an empty string whenever the
argument is not a string, consistent with how the split step already
treats non-string input.
Copilot AI review requested due to automatic review settings June 23, 2026 12:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants