This error occurs when you pass an invalid depth value to Node.js util.inspect() function. The depth parameter must be a non-negative integer (0 or greater), null, or Infinity—passing a negative number triggers this RangeError.
The RangeError for util.inspect depth occurs when the depth option is set to a negative number. The depth parameter controls how many levels deep Node.js will recurse when inspecting an object for debugging or logging purposes. Node.js util.inspect() is commonly used to convert objects to human-readable strings, and the depth option limits recursion to prevent overwhelming output on deeply nested structures. Valid values are non-negative integers (0, 1, 2, etc.), null (infinite depth), or Infinity. When a negative value is passed—whether directly or through a variable—Node.js immediately throws this RangeError to prevent invalid behavior. This error typically happens when depth values are computed dynamically, come from configuration files without validation, or result from mathematical operations that produce negative results. It's a runtime validation error that ensures the depth parameter stays within sensible bounds.
Check the stack trace to identify where util.inspect is being called with an invalid depth:
RangeError: util.inspect depth must be a non-negative integer
at formatValue (node:internal/util/inspect:482:11)
at inspect (node:internal/util/inspect:254:10)
at /path/to/your/file.js:25:18Look at line 25 in your file to see how the depth option is being set.
Add console.log or use a debugger to verify what depth value is being passed:
const util = require('util');
const myObject = { a: { b: { c: 'deep' } } };
const depth = getDepthFromConfig(); // Could be negative
console.log('depth value:', depth);
console.log('depth type:', typeof depth);
// This might fail if depth is negative
console.log(util.inspect(myObject, { depth }));Use console.log to inspect the actual depth value before it's used.
Validate the depth parameter before passing it to util.inspect:
const util = require('util');
// ❌ BAD - depth might be negative
let depth = config.inspectDepth;
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Validate depth is non-negative
let depth = config.inspectDepth;
if (typeof depth === 'number' && depth < 0) {
console.warn('Invalid depth value, using default of 2');
depth = 2;
}
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Use Math.max to ensure non-negative
let depth = Math.max(0, config.inspectDepth);
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Use default with fallback
let depth = config.inspectDepth ?? 2;
if (typeof depth !== 'number' || depth < 0) {
depth = 2;
}
console.log(util.inspect(obj, { depth }));Always check that depth is a non-negative number before use.
If you're calculating depth dynamically, add bounds checking:
const util = require('util');
// ❌ BAD - Subtraction can go negative
const maxDepth = 3;
const currentLevel = 5;
const depth = maxDepth - currentLevel; // Results in -2
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Use Math.max to prevent negative values
const maxDepth = 3;
const currentLevel = 5;
const depth = Math.max(0, maxDepth - currentLevel); // Results in 0
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Check before use
let depth = maxDepth - currentLevel;
if (depth < 0) {
console.warn('Depth calculation resulted in negative value, using 0');
depth = 0;
}
console.log(util.inspect(obj, { depth }));Use Math.max(0, ...) to ensure calculations never go negative.
When loading depth from config files or environment variables, validate the value:
const util = require('util');
// ❌ BAD - No validation
const depth = parseInt(process.env.INSPECT_DEPTH);
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Validate and provide safe default
function getValidDepth(value, defaultDepth = 2) {
const parsed = parseInt(value, 10);
if (isNaN(parsed) || parsed < 0) {
console.warn(`Invalid depth value: ${value}, using default: ${defaultDepth}`);
return defaultDepth;
}
return parsed;
}
const depth = getValidDepth(process.env.INSPECT_DEPTH);
console.log(util.inspect(obj, { depth }));
// ✓ GOOD - Use schema validation with Zod
import { z } from 'zod';
const ConfigSchema = z.object({
inspectDepth: z.number().int().nonnegative().default(2),
});
const config = ConfigSchema.parse({
inspectDepth: process.env.INSPECT_DEPTH,
});
console.log(util.inspect(obj, { depth: config.inspectDepth }));Always validate external configuration values before using them.
Node.js accepts null or Infinity for unlimited depth:
const util = require('util');
// ✓ Non-negative integer
console.log(util.inspect(obj, { depth: 0 })); // Shallow
console.log(util.inspect(obj, { depth: 2 })); // Default
console.log(util.inspect(obj, { depth: 10 })); // Deep
// ✓ Infinite depth (use with caution on circular objects)
console.log(util.inspect(obj, { depth: null }));
console.log(util.inspect(obj, { depth: Infinity }));
// ❌ Invalid values
console.log(util.inspect(obj, { depth: -1 })); // RangeError
console.log(util.inspect(obj, { depth: -5 })); // RangeError
console.log(util.inspect(obj, { depth: 1.5 })); // Works but should be integerUse null or Infinity for unlimited inspection, but be careful with circular references.
Understanding util.inspect Depth Parameter
The depth parameter in util.inspect determines how many levels of nested properties Node.js will show before replacing deeper objects with [Object] or [Array]. This is crucial for performance and readability:
const util = require('util');
const deepObject = {
level1: {
level2: {
level3: {
level4: 'deep value'
}
}
}
};
// Depth 0: Only top level
console.log(util.inspect(deepObject, { depth: 0 }));
// Output: { level1: [Object] }
// Depth 1: One level deep
console.log(util.inspect(deepObject, { depth: 1 }));
// Output: { level1: { level2: [Object] } }
// Depth 2: Two levels deep (default)
console.log(util.inspect(deepObject, { depth: 2 }));
// Output: { level1: { level2: { level3: [Object] } } }
// Depth null: Infinite (shows everything)
console.log(util.inspect(deepObject, { depth: null }));
// Output: { level1: { level2: { level3: { level4: 'deep value' } } } }Performance Considerations
Setting depth to null or Infinity on large objects can cause:
- Excessive memory usage
- Slow string conversion
- Console flooding
- Stack overflow on circular references
Always use appropriate depth limits for production logging.
Custom Inspection Functions
When implementing custom [util.inspect.custom] methods, properly handle depth:
const util = require('util');
class MyClass {
constructor() {
this.nested = { data: { deep: 'value' } };
}
[util.inspect.custom](depth, options) {
// Check if depth limit reached
if (depth < 0) {
return options.stylize('[MyClass]', 'special');
}
// Decrease depth for nested inspection
const newOptions = Object.assign({}, options, {
depth: options.depth === null ? null : options.depth - 1
});
return `MyClass ${util.inspect(this.nested, newOptions)}`;
}
}
const instance = new MyClass();
console.log(util.inspect(instance, { depth: 1 }));This pattern ensures your custom inspection respects depth limits.
Console.dir and Depth
The console.dir() method uses util.inspect internally and also accepts depth:
const obj = { a: { b: { c: 'd' } } };
// These are equivalent
console.dir(obj, { depth: 3 });
console.log(util.inspect(obj, { depth: 3 }));
// Default depth for console.dir is 2
console.dir(obj); // Same as { depth: 2 }Depth in Logging Libraries
Popular logging libraries (Winston, Bunyan, Pino) often use util.inspect internally:
// Winston example
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.printf(info => {
// Ensure depth is valid when inspecting
const depth = process.env.LOG_DEPTH ? Math.max(0, parseInt(process.env.LOG_DEPTH, 10)) : 3;
return util.inspect(info, { depth, colors: true });
}),
transports: [new winston.transports.Console()]
});Always validate depth configuration in logging setup.
Debugging Depth Issues
Create a helper function to safely inspect with configurable depth:
const util = require('util');
function safeInspect(obj, options = {}) {
const defaults = {
depth: 2,
colors: true,
maxArrayLength: 100,
breakLength: 80,
compact: false
};
// Merge options and validate depth
const mergedOptions = { ...defaults, ...options };
if (typeof mergedOptions.depth === 'number' && mergedOptions.depth < 0) {
console.warn('Negative depth provided, using 0');
mergedOptions.depth = 0;
}
return util.inspect(obj, mergedOptions);
}
// Safe to use with any depth value
console.log(safeInspect(obj, { depth: -5 })); // Warns and uses 0
console.log(safeInspect(obj, { depth: 10 })); // Works fine
console.log(safeInspect(obj, { depth: null })); // Infinite depthThis wrapper prevents RangeError and provides sensible defaults.
TypeScript Type Safety
Define proper types for inspect options:
import util from 'util';
interface SafeInspectOptions {
depth?: number | null; // Non-negative or null
colors?: boolean;
compact?: boolean;
}
function safeInspect(obj: unknown, options: SafeInspectOptions = {}): string {
const depth = options.depth ?? 2;
// Type guard ensures depth is valid
if (typeof depth === 'number' && depth < 0) {
throw new Error('Depth must be non-negative');
}
return util.inspect(obj, { ...options, depth });
}TypeScript helps catch invalid depth values at compile time.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files