petkaantonov/bluebird · error · TypeError
onCancel must be a function, got: %s
Error message
onCancel must be a function, got: %s
What it means
When an executor uses Bluebird's third executor argument (the onCancel registration callback), the value passed must be a function. cancellationExecute validates typeof onCancel and throws a TypeError('onCancel must be a function, got: %s') otherwise. The onCancel callback is what Bluebird invokes to roll back work when the promise is cancelled, so a non-function is always a caller bug.
Source
Thrown at src/debuggability.js:414
Promise.prototype._setOnCancel = function (handler) { USE(handler); };
Promise.prototype._attachCancellationCallback = function(onCancel) {
USE(onCancel);
};
Promise.prototype._captureStackTrace = function () {};
Promise.prototype._attachExtraTrace = function () {};
Promise.prototype._dereferenceTrace = function () {};
Promise.prototype._clearCancellationData = function() {};
Promise.prototype._propagateFrom = function (parent, flags) {
USE(parent);
USE(flags);
};
function cancellationExecute(executor, resolve, reject) {
var promise = this;
try {
executor(resolve, reject, function(onCancel) {
if (typeof onCancel !== "function") {
throw new TypeError("onCancel must be a function, got: " +
util.toString(onCancel));
}
promise._attachCancellationCallback(onCancel);
});
} catch (e) {
return e;
}
}
function cancellationAttachCancellationCallback(onCancel) {
if (!this._isCancellable()) return this;
var previousOnCancel = this._onCancel();
if (previousOnCancel !== undefined) {
if (util.isArray(previousOnCancel)) {
previousOnCancel.push(onCancel);
} else {
this._setOnCancel([previousOnCancel, onCancel]);View on GitHub (pinned to c220cfe480)
Solutions
- Pass an actual function: onCancel(() => cleanup())
- Check the variable passed to onCancel is defined and a function (typeof x === 'function')
- Remember onCancel takes a callback, not a reason string — move reason into your own closure state
- Wrap executor body so a bad onCancel call rejects rather than crashes the constructor
Example fix
// before
new Promise((resolve, reject, onCancel) => {
onCancel('request aborted'); // not a function
});
// after
new Promise((resolve, reject, onCancel) => {
onCancel(() => socket.close('request aborted'));
}); Defensive patterns
Strategy: type-guard
Validate before calling
new Promise((resolve, reject, onCancel) => {
const cb = () => cleanup();
if (typeof cb !== 'function') throw new TypeError('onCancel callback must be a function');
onCancel(cb);
...
}); Type guard
function isOnCancelCallback(fn) {
return typeof fn === 'function';
} Try / catch
new Promise((resolve, reject, onCancel) => {
try {
onCancel(() => rollback());
doWork(resolve, reject);
} catch (e) {
reject(e);
}
}); Prevention
- Always pass a closure to onCancel, never a value or reason string
- Keep the rollback logic in a named function so its type is obvious
- Lint executor callbacks for onCancel argument usage
- Add a unit test that cancels a promise to exercise the onCancel path
When it happens
Trigger: new Promise((resolve, reject, onCancel) => { onCancel(someNonFunction) }) — e.g. onCancel(someVar) where someVar is undefined, null, a number, or the result of a misreferenced identifier; also passing a truthy non-function like a promise or config object.
Common situations: Passing the wrong variable to onCancel; expecting onCancel to accept a value/reason instead of a function; typos like onCancel(this.cleanup) where cleanup is undefined; copy-paste errors when adding cancellation support to executors.
Related errors
- Object %s has no method '%s'
- cannot enable cancellation after promises are in use
- generatorFunction must be a function See http://goo.gl/
- expecting a function but got %s
- expecting a function but got %s
AI-assisted analysis of petkaantonov/bluebird@c220cfe480 (2026-09-02).
Data as JSON: /api/errors/eba1e8d16c5b7207.
Report an issue: GitHub.