gorhill/uBlock · error · Error

Pending useLists() operation

Error message

Pending useLists() operation

What it means

useLists() is a re-entrancy-guarded async operation over the shared module-level snfe engine. A module-level flag, useLists.promise (initialized to null at index.js:200), is assigned the in-flight Promise.all while lists compile/load (index.js:191) and reset to null only after that await settles (index.js:193). Invoking useLists() again while the flag is still non-null throws immediately, which prevents two concurrent list-loading passes from racing on the same engine state.

Source

Thrown at platform/nodejs/index.js:161

        if ( parser.isFilter() === false ) { continue; }
        if ( parser.isNetworkFilter() === false ) { continue; }
        if ( compiler.compile(parser, writer) ) { continue; }
        if ( compiler.error !== undefined && events !== undefined ) {
            options.events.push({
                type: 'error',
                text: compiler.error
            });
        }
    }

    return writer.toString();
}

/******************************************************************************/

async function useLists(lists, options = {}) {
    if ( useLists.promise !== null ) {
        throw new Error('Pending useLists() operation');
    }

    // Remove all filters
    snfe.reset();

    if ( Array.isArray(lists) === false || lists.length === 0 ) {
        return;
    }

    let compiler = null;

    const consumeList = list => {
        let { compiled } = list;
        if ( typeof compiled !== 'string' || compiled === '' ) {
            const writer = new CompiledListWriter();
            if ( compiler === null ) {
                compiler = snfe.createCompiler();
            }

View on GitHub (pinned to c68df492fd)

Solutions

  1. Always `await engine.useLists(lists)` before starting a second call; treat list loading as strictly serial.
  2. Serialize updates with a caller-side queue or mutex so a new request waits for the prior one instead of racing it.
  3. If you are calling StaticNetFilteringEngine.release(), make sure no earlier useLists() is still pending, since release awaits useLists([]) internally.
  4. Resolve list entry contents before invoking rather than passing live Promises, so the batch settles predictably.

Example fix

// before
engine.useLists(lists);        // not awaited
engine.useLists(otherLists);   // throws: Pending useLists() operation

// after
await engine.useLists(lists);
await engine.useLists(otherLists);   // strictly serial
Defensive patterns

Strategy: validation

Validate before calling

// Serialize list loads at the caller; never overlap useLists() calls.
let pending = null;
async function safeUseLists(engine, lists) {
  if (pending) await pending;                 // wait for prior load to settle
  const p = engine.useLists(lists);
  pending = p;
  try { return await p; }
  finally { pending = null; }
}

Try / catch

// Catch only to surface that a load is in flight; do NOT blindly retry the
// same call, which can re-trigger the guard. Serialize and await first.
try {
  await engine.useLists(lists);
} catch (err) {
  if (err && /Pending useLists\(\) operation/.test(err.message)) {
    throw new Error('List load already in progress; await it before retrying.');
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling engine.useLists(lists) a second time before the first awaited call has settled. The instance method delegates straight to the module-level useLists (index.js:216), so any overlap trips the guard. It also fires when list entries are themselves unresolved Promises: the loop wraps each in Promise.resolve (index.js:187-189), so the overall Promise.all stays pending and a second useLists() call lands while it is in flight.

Common situations: Reloading filter lists from an event handler without awaiting the previous reload; dispatching two updates in quick succession; calling engine.useLists() fire-and-forget; or calling StaticNetFilteringEngine.release() (which itself awaits useLists([]) at index.js:277) while another load is still running.

Related errors


AI-assisted analysis of gorhill/uBlock@c68df492fd (2026-08-12). Data as JSON: /api/errors/6f5d39d12878a056. Report an issue: GitHub.