{"record":{"id":"aed65a8e07f7b3b8","repo":"eyaltoledano/claude-task-master","slug":"onprogress-must-be-a-function","errorCode":null,"errorMessage":"onProgress must be a function","messagePattern":"onProgress must be a function","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utils/stream-parser.js","lineNumber":71,"sourceCode":"\n\tvalidate() {\n\t\tif (!this.jsonPaths || !Array.isArray(this.jsonPaths)) {\n\t\t\tthrow new Error('jsonPaths is required and must be an array');\n\t\t}\n\t\tif (this.jsonPaths.length === 0) {\n\t\t\tthrow new Error('jsonPaths array cannot be empty');\n\t\t}\n\t\tif (this.maxBufferSize <= 0) {\n\t\t\tthrow new Error('maxBufferSize must be positive');\n\t\t}\n\t\tif (this.expectedTotal < 0) {\n\t\t\tthrow new Error('expectedTotal cannot be negative');\n\t\t}\n\t\tif (this.estimateTokens && typeof this.estimateTokens !== 'function') {\n\t\t\tthrow new Error('estimateTokens must be a function');\n\t\t}\n\t\tif (this.onProgress && typeof this.onProgress !== 'function') {\n\t\t\tthrow new Error('onProgress must be a function');\n\t\t}\n\t\tif (this.onError && typeof this.onError !== 'function') {\n\t\t\tthrow new Error('onError must be a function');\n\t\t}\n\t\tif (\n\t\t\tthis.fallbackItemExtractor &&\n\t\t\ttypeof this.fallbackItemExtractor !== 'function'\n\t\t) {\n\t\t\tthrow new Error('fallbackItemExtractor must be a function');\n\t\t}\n\t\tif (this.itemValidator && typeof this.itemValidator !== 'function') {\n\t\t\tthrow new Error('itemValidator must be a function');\n\t\t}\n\t}\n\n\tstatic defaultItemValidator(item) {\n\t\treturn (\n\t\t\titem && item.title && typeof item.title === 'string' && item.title.trim()","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/src/utils/stream-parser.js#L53-L89","documentation":"The onProgress option must be a callable invoked with parsing progress updates. The constructor throws if onProgress is truthy but not a function, catching wiring mistakes before streaming starts.","triggerScenarios":"new StreamParser({ jsonPaths: [...], onProgress: true }), onProgress: 'updateProgress' (string), or spreading a config object where onProgress holds a serialized/non-callable value.","commonSituations":"Passing a callback name instead of the callback; an async wrapper imported incorrectly (module object, not function); config-driven setup where callbacks cannot be serialized.","solutions":["Pass a real function: onProgress: (progress) => console.log(progress.percent).","If wiring from config, resolve the callback reference before constructing.","Remove onProgress if progress reporting is unnecessary."],"exampleFix":"// before\nnew StreamParser({ jsonPaths: ['$.items'], onProgress: 'logProgress' })\n// after\nnew StreamParser({ jsonPaths: ['$.items'], onProgress: (p) => console.log(`${p.parsed}/${p.total}`) })","handlingStrategy":"type-guard","validationCode":"if (options.onProgress != null && typeof options.onProgress !== 'function') {\n  throw new TypeError('onProgress must be a function');\n}","typeGuard":"function isCallable(v) {\n  return typeof v === 'function';\n}","tryCatchPattern":"try {\n  return new StreamParser(options);\n} catch (err) {\n  if (err.message === 'onProgress must be a function') {\n    const { onProgress, ...rest } = options;\n    return new StreamParser(rest);\n  }\n  throw err;\n}","preventionTips":["Bind methods when passing object methods: onProgress: obj.onProgress.bind(obj).","Don't pass callback names/strings from config; resolve references first.","Keep callbacks out of serialized config; inject them in code."],"tags":["configuration","validation","callbacks"],"backgroundTag":"wrong-argument-type","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}