auth0/node-jsonwebtoken · error

Expected "${parameterName}" to be a plain object.

Error message

Expected "${parameterName}" to be a plain object.

What it means

jwt.sign() validates its payload and options through a schema-driven validate() function. Both must be plain JavaScript objects (prototype Object, not arrays, strings, Maps, class instances from other realms, or null); otherwise the library throws before doing any signing work.

Source

Thrown at sign.js:44

  issuer: { isValid: isString, message: '"issuer" must be a string' },
  subject: { isValid: isString, message: '"subject" must be a string' },
  jwtid: { isValid: isString, message: '"jwtid" must be a string' },
  noTimestamp: { isValid: isBoolean, message: '"noTimestamp" must be a boolean' },
  keyid: { isValid: isString, message: '"keyid" must be a string' },
  mutatePayload: { isValid: isBoolean, message: '"mutatePayload" must be a boolean' },
  allowInsecureKeySizes: { isValid: isBoolean, message: '"allowInsecureKeySizes" must be a boolean'},
  allowInvalidAsymmetricKeyTypes: { isValid: isBoolean, message: '"allowInvalidAsymmetricKeyTypes" must be a boolean'}
};

const registered_claims_schema = {
  iat: { isValid: isNumber, message: '"iat" should be a number of seconds' },
  exp: { isValid: isNumber, message: '"exp" should be a number of seconds' },
  nbf: { isValid: isNumber, message: '"nbf" should be a number of seconds' }
};

function validate(schema, allowUnknown, object, parameterName) {
  if (!isPlainObject(object)) {
    throw new Error('Expected "' + parameterName + '" to be a plain object.');
  }
  Object.keys(object)
    .forEach(function(key) {
      const validator = schema[key];
      if (!validator) {
        if (!allowUnknown) {
          throw new Error('"' + key + '" is not allowed in "' + parameterName + '"');
        }
        return;
      }
      if (!validator.isValid(object[key])) {
        throw new Error(validator.message);
      }
    });
}

function validateOptions(options) {
  return validate(sign_options_schema, false, options, 'options');

View on GitHub (pinned to b924272f29)

Solutions

  1. Pass the payload as a plain object, e.g. JSON.parse(rawJson) instead of the raw string
  2. Omit the options argument entirely rather than passing null/undefined-wrapped values
  3. If the payload is a Buffer/string primitive, wrap intentionally or use a plain object ({ data: value })
  4. Ensure the object isn't constructed in another realm (e.g. vm sandbox) — clone with {...obj}

Example fix

// before
const token = jwt.sign(JSON.stringify(payload), secret, null);
// after
const token = jwt.sign(payload, secret, { expiresIn: '1h' });
Defensive patterns

Strategy: type-guard

Validate before calling

function isPlainObject(v) {
  if (v === null || typeof v !== 'object' || Array.isArray(v)) return false;
  const proto = Object.getPrototypeOf(v);
  return proto === Object.prototype || proto === null;
}
if (!isPlainObject(payload)) throw new TypeError('payload must be a plain object');
if (options !== undefined && !isPlainObject(options)) throw new TypeError('options must be a plain object');

Type guard

function isPlainObject(v) {
  return typeof v === 'object' && v !== null && !Array.isArray(v) &&
    (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
}

Try / catch

try {
  token = jwt.sign(payload, secret, options);
} catch (err) {
  if (/to be a plain object/.test(err.message)) {
    payload = isPlainObject(payload) ? payload : JSON.parse(String(payload));
    options = isPlainObject(options) ? options : {};
    token = jwt.sign(payload, secret, options);
  } else throw err;
}

Prevention

When it happens

Trigger: jwt.sign('string-payload', secret), jwt.sign(payload, secret, ['array']), passing a Buffer or null as payload/options, or passing a non-plain object created in another vm/context.

Common situations: Passing JSON strings instead of parsed objects (forgetting JSON.parse); passing undefined/null options; passing an object deserialized via a library that produces non-plain prototypes; sending a Buffer payload expecting binary JWT support.

Related errors


AI-assisted analysis of auth0/node-jsonwebtoken@b924272f29 (2026-09-02). Data as JSON: /api/errors/11e2ce0cadd98ce6. Report an issue: GitHub.