sequelize/sequelize · error · Error
The argument passed to findOne must be an options object, us
Error message
The argument passed to findOne must be an options object, use findByPk if you wish to pass a single primary key value
What it means
Thrown by Model.findOne() when its single argument is defined but is not a plain object (e.g. a number, string, or array). findOne only accepts an options object; passing a primary key value is a common pre-v5 mistake that newer Sequelize rejects. To look up a row by its primary key you must call findByPk instead. The guard runs before any query is built, so no SQL is executed.
Source
Thrown at packages/core/src/model.js:1570
}
}),
);
return original;
}
/**
* Search for a single instance.
*
* Returns the first instance corresponding matching the query.
* If not found, returns null or throws an error if {@link FindOptions.rejectOnEmpty} is set.
*
* @param {object} [options] A hash of options to describe the scope of the search
* @returns {Promise<Model|null>}
*/
static async findOne(options) {
if (options !== undefined && !isPlainObject(options)) {
throw new Error(
'The argument passed to findOne must be an options object, use findByPk if you wish to pass a single primary key value',
);
}
options = cloneDeep(options) ?? {};
// findOne only ever needs one result
// conditional temporarily fixes 14618
// https://github.com/sequelize/sequelize/issues/14618
if (options.limit === undefined) {
options.limit = 1;
}
// Bypass a possible overloaded findAll.
return await Model.findAll.call(
this,
defaultsLodash(options, {
model: this,
plain: true,View on GitHub (pinned to 7e1deec499)
Solutions
- Replace findOne(pkValue) with findByPk(pkValue) when you have a primary key.
- Wrap the value in an options object: findOne({ where: { id: value } }).
- Add a runtime guard that routes numbers/strings to findByPk and objects to findOne.
- Audit call sites upgraded across major versions; the v5 upgrade guide lists this breaking change.
Example fix
// before
const user = await User.findOne(42);
// after
const user = await User.findByPk(42);
// or
const user = await User.findOne({ where: { id: 42 } }); Defensive patterns
Strategy: validation
Validate before calling
import { isPlainObject } from '@sequelize/core';
function safeFindOne(Model, arg) {
if (arg === undefined) return Model.findOne();
if (typeof arg === 'number' || typeof arg === 'string' || Buffer.isBuffer(arg)) {
return Model.findByPk(arg);
}
if (!isPlainObject(arg)) {
throw new TypeError('findOne expects an options object or a PK for findByPk');
}
return Model.findOne(arg);
} Type guard
function isFindOneOptions(v): v is object {
return v === undefined || (typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof Date) && !(v instanceof Buffer));
} Prevention
- Never pass a raw primary key to findOne; route PKs through findByPk.
- Lint for findOne(<literal number|string>) calls.
- After a major-version upgrade, grep for findOne(\d) and findOne('...').
When it happens
Trigger: Calling User.findOne(123), User.findOne('abc'), or User.findOne([1,2]). Also triggered by passing a Sequelize.literal() or any non-plain-object (class instance, Formidable form, etc.) as the only argument. Note: findOne(undefined) and findOne() are allowed.
Common situations: Upgrading from Sequelize v3/v4 where findOne accepted a plain PK number. Migrating from Mongoose whose findById semantics differ. Passing a value computed at runtime that is unexpectedly null-coerced to a number or that comes back as a wrapped type (e.g. BigNumber, ObjectId).
Related errors
- The argument passed to findAndCountAll must be an options ob
- Missing where attribute in the options parameter passed to f
- Missing where attribute in the options parameter passed to f
- The argument passed to findAll must be an options object, us
- The attributes option must be an array of column names or an
AI-assisted analysis of sequelize/sequelize@7e1deec499 (2026-08-03).
Data as JSON: /data/errors/fc238ff5f8a30185.json.
Report an issue: GitHub.