{"record":{"id":"6f5d39d12878a056","repo":"gorhill/uBlock","slug":"pending-uselists-operation","errorCode":null,"errorMessage":"Pending useLists() operation","messagePattern":"Pending useLists\\(\\) operation","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"platform/nodejs/index.js","lineNumber":161,"sourceCode":"        if ( parser.isFilter() === false ) { continue; }\n        if ( parser.isNetworkFilter() === false ) { continue; }\n        if ( compiler.compile(parser, writer) ) { continue; }\n        if ( compiler.error !== undefined && events !== undefined ) {\n            options.events.push({\n                type: 'error',\n                text: compiler.error\n            });\n        }\n    }\n\n    return writer.toString();\n}\n\n/******************************************************************************/\n\nasync function useLists(lists, options = {}) {\n    if ( useLists.promise !== null ) {\n        throw new Error('Pending useLists() operation');\n    }\n\n    // Remove all filters\n    snfe.reset();\n\n    if ( Array.isArray(lists) === false || lists.length === 0 ) {\n        return;\n    }\n\n    let compiler = null;\n\n    const consumeList = list => {\n        let { compiled } = list;\n        if ( typeof compiled !== 'string' || compiled === '' ) {\n            const writer = new CompiledListWriter();\n            if ( compiler === null ) {\n                compiler = snfe.createCompiler();\n            }","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/gorhill/uBlock/blob/c68df492fd951bc3355fd0c6fec4afa8e2eae0aa/platform/nodejs/index.js#L143-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Always `await engine.useLists(lists)` before starting a second call; treat list loading as strictly serial.","Serialize updates with a caller-side queue or mutex so a new request waits for the prior one instead of racing it.","If you are calling StaticNetFilteringEngine.release(), make sure no earlier useLists() is still pending, since release awaits useLists([]) internally.","Resolve list entry contents before invoking rather than passing live Promises, so the batch settles predictably."],"exampleFix":"// before\nengine.useLists(lists);        // not awaited\nengine.useLists(otherLists);   // throws: Pending useLists() operation\n\n// after\nawait engine.useLists(lists);\nawait engine.useLists(otherLists);   // strictly serial","handlingStrategy":"validation","validationCode":"// Serialize list loads at the caller; never overlap useLists() calls.\nlet pending = null;\nasync function safeUseLists(engine, lists) {\n  if (pending) await pending;                 // wait for prior load to settle\n  const p = engine.useLists(lists);\n  pending = p;\n  try { return await p; }\n  finally { pending = null; }\n}","typeGuard":null,"tryCatchPattern":"// Catch only to surface that a load is in flight; do NOT blindly retry the\n// same call, which can re-trigger the guard. Serialize and await first.\ntry {\n  await engine.useLists(lists);\n} catch (err) {\n  if (err && /Pending useLists\\(\\) operation/.test(err.message)) {\n    throw new Error('List load already in progress; await it before retrying.');\n  }\n  throw err;\n}","preventionTips":["Treat engine.useLists() as strictly serial: always await it before the next call.","Keep a single caller-side pending flag/queue so updates never overlap.","Resolve list entry contents to plain objects before passing them in.","Before calling StaticNetFilteringEngine.release(), ensure no useLists() is still running."],"tags":["concurrency","async","reentrancy","nodejs"],"backgroundTag":null,"analyzedSha":"c68df492fd951bc3355fd0c6fec4afa8e2eae0aa","analyzedAt":"2026-08-12T23:57:11.071Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}