{"record":{"id":"aa55334163180272","repo":"caolan/async","slug":"callback-was-already-called","errorCode":null,"errorMessage":"Callback was already called.","messagePattern":"Callback was already called\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/internal/onlyOnce.js","lineNumber":3,"sourceCode":"export default function onlyOnce(fn) {\n    return function (...args) {\n        if (fn === null) throw new Error(\"Callback was already called.\");\n        var callFn = fn;\n        fn = null;\n        callFn.apply(this, args);\n    };\n}\n","sourceCodeStart":1,"sourceCodeEnd":9,"githubUrl":"https://github.com/caolan/async/blob/13dfaf13f3fc809ba1c9c39d5e267ac6959cbf4a/lib/internal/onlyOnce.js#L1-L9","documentation":"onlyOnce wraps a callback so it can only be invoked a single time; a second invocation throws 'Callback was already called.' This protects control-flow helpers (each, whilst, queue, etc.) from double-completion, which would otherwise corrupt results or call your final callback twice.","triggerScenarios":"Calling the done/callback parameter twice in an iteratee or worker — e.g. calling cb(err) inside a try/catch and then calling cb again, or invoking cb both in a promise .then and .catch, or calling the outer callback and an internal one.","commonSituations":"Mixing promise resolution with manual callback invocation, fire-and-forget async calls that later invoke the shared callback, error paths that fall through to a second cb call, using the same callback for multiple async operations.","solutions":["Ensure each callback path calls cb exactly once and returns immediately after","Guard with a done flag or use async's once utility around your own logic","Don't mix promises and callbacks on the same completion path","Check error branches: after cb(err) add return so code doesn't continue to cb(null)"],"exampleFix":"// before\nfs.readFile(path, (err, data) => {\n  if (err) cb(err);\n  cb(null, data); // called twice on error\n});\n// after\nfs.readFile(path, (err, data) => {\n  if (err) return cb(err);\n  cb(null, data);\n});","handlingStrategy":"try-catch","validationCode":"function wrapSingleCall(cb) {\n  let called = false;\n  return (...args) => {\n    if (called) throw new Error('Callback was already called.');\n    called = true;\n    return cb(...args);\n  };\n}","typeGuard":null,"tryCatchPattern":"iteratee = (item, cb) => {\n  const onceCb = once((err) => result(err));\n  try {\n    doWork(item, onceCb);\n  } catch (err) {\n    onceCb(err);\n  }\n};","preventionTips":["Return immediately after every cb(...) call, especially in error branches","Never mix promise resolution and manual callback invocation on one path","Audit iteratees for code paths that can reach cb twice"],"tags":["async","callback","double-invoke"],"backgroundTag":"callback-called-twice","analyzedSha":"13dfaf13f3fc809ba1c9c39d5e267ac6959cbf4a","analyzedAt":"2026-08-28T22:50:46.507Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}