petkaantonov/bluebird · error · TypeError

Cannot promisify an API that has normal methods with '%s'-su

Error message

Cannot promisify an API that has normal methods with '%s'-suffix

    See http://goo.gl/MqrFmX

What it means

`Promise.promisifyAll` cannot promisify an object that contains both a method named `fooAsync` (matching the suffix) and a plain method `foo`, because generating `fooAsync` would collide with the existing normal method. The `checkValid` helper (src/promisify.js:59) detects this and throws.

Source

Thrown at src/promisify.js:59

    }
}

function hasPromisified(obj, key, suffix) {
    var val = util.getDataPropertyOrDefault(obj, key + suffix,
                                            defaultPromisified);
    return val ? isPromisified(val) : false;
}
function checkValid(ret, suffix, suffixRegexp) {
    // Verify that in the list of methods to promisify there is no
    // method that has a name ending in "Async"-suffix while
    // also having a method with the same name but no Async suffix
    for (var i = 0; i < ret.length; i += 2) {
        var key = ret[i];
        if (suffixRegexp.test(key)) {
            var keyWithoutAsyncSuffix = key.replace(suffixRegexp, "");
            for (var j = 0; j < ret.length; j += 2) {
                if (ret[j] === keyWithoutAsyncSuffix) {
                    throw new TypeError(PROMISIFICATION_NORMAL_METHODS_ERROR
                        .replace("%s", suffix));
                }
            }
        }
    }
}

function promisifiableMethods(obj, suffix, suffixRegexp, filter) {
    var keys = util.inheritedDataKeys(obj);
    var ret = [];
    for (var i = 0; i < keys.length; ++i) {
        var key = keys[i];
        var value = obj[key];
        var passesDefaultFilter = filter === defaultFilter
            ? true : defaultFilter(key, value, obj);
        if (typeof value === "function" &&
            !isPromisified(value) &&
            !hasPromisified(obj, key, suffix) &&

View on GitHub (pinned to c220cfe480)

Solutions

  1. Use a different suffix that doesn't collide: `Promise.promisifyAll(obj, { suffix: 'Promised' })`.
  2. Pass a `filter` to exclude the conflicting methods from promisification.
  3. Promisify only specific methods manually with `Promise.promisify`.

Example fix

// before
Promise.promisifyAll(fs); // fs has read and readAsync
// after
Promise.promisifyAll(fs, { suffix: 'Promised' });
Defensive patterns

Strategy: validation

Validate before calling

const keys = Object.keys(target).filter(k => typeof target[k] === 'function');
const suffixed = keys.filter(k => k.endsWith('Async')).map(k => k.slice(0, -'Async'.length));
if (suffixed.some(base => keys.includes(base))) {
  throw new Error('promisifyAll suffix conflict detected');
}
Promise.promisifyAll(target, { suffix: 'Async' });

Try / catch

try {
  Promise.promisifyAll(target);
} catch (e) {
  if (String(e).includes('normal methods')) {
    target = Promise.promisifyAll(target, { suffix: 'Promised' });
  } else throw e;
}

Prevention

When it happens

Trigger: `Promise.promisifyAll(obj)` where obj has methods `read` and `readAsync` with default 'Async' suffix; using a custom suffix that collides similarly (`suffix: 'Sync'` on an API with `readSync` and `read`).

Common situations: Promisifying libraries that already ship async-suffixed methods (some fs wrappers, AWS clients); switching suffixes without checking existing method names.

Related errors


AI-assisted analysis of petkaantonov/bluebird@c220cfe480 (2026-09-02). Data as JSON: /api/errors/867c7dffe741d46b. Report an issue: GitHub.