{"record":{"id":"1237d07fe9f37db8","repo":"hexojs/hexo","slug":"fn-must-be-a-function","errorCode":null,"errorMessage":"fn must be a function","messagePattern":"fn must be a function","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/box/index.ts","lineNumber":100,"sourceCode":"          path: this.source\n        }, options);\n      }\n    }\n\n    _File.prototype.box = this;\n\n    return _File;\n  }\n\n  addProcessor(pattern: (...args: any[]) => any): void;\n  addProcessor(pattern: string | RegExp | Pattern | ((str: string) => any), fn: (...args: any[]) => any): void;\n  addProcessor(pattern: string | RegExp | Pattern | ((str: string) => any), fn?: (...args: any[]) => any): void {\n    if (!fn && typeof pattern === 'function') {\n      fn = pattern;\n      pattern = defaultPattern;\n    }\n\n    if (typeof fn !== 'function') throw new TypeError('fn must be a function');\n    if (!(pattern instanceof Pattern)) pattern = new Pattern(pattern);\n\n    this.processors.push({\n      pattern,\n      process: fn\n    });\n  }\n\n  _readDir(base: string, prefix = ''): BlueBirdPromise<string[]> {\n    const { context: ctx } = this;\n    const results: string[] = [];\n    return readDirWalker(ctx, base, results, this.ignore, prefix)\n      .return(results)\n      .map(path => this._checkFileStatus(path))\n      .map(file => this._processFile(file.type, file.path).return(file.path));\n  }\n\n  _checkFileStatus(path: string): { type: string; path: string } {","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/hexojs/hexo/blob/059cb17494d0632a053c24077f5bcb6ae92acc51/lib/box/index.ts#L82-L118","documentation":"Thrown by Box.addProcessor when the resolved processor callback is not a function. Hexo's Box registers functions that process files matched by a pattern; the call form shifts arguments so a lone function arg becomes the processor with the default pattern. If no usable function is found, this guard rejects the registration as a programmer error.","triggerScenarios":"Calling box.addProcessor('/\\.md$/') (pattern string with no second fn argument), box.addProcessor(undefined), or box.addProcessor(pattern, null) where the second argument is not a function.","commonSituations":"A plugin or theme script registers a processor but the imported handler resolved to undefined (bad import path, cyclic import), or the author passed a config object instead of a function, or forgot the callback in the two-argument form.","solutions":["Pass the processor as a function: box.addProcessor(/\\.md$/, file => { ... }) or box.addProcessor(file => { ... }).","Verify the imported handler symbol is defined and is a function before registering (check for undefined from a broken import).","If using the pattern+fn form, ensure the SECOND argument is the function, not a string/object."],"exampleFix":"// before\nbox.addProcessor(/\\.md$/, undefined);\n// after\nbox.addProcessor(/\\.md$/, file => { /* ... */ });","handlingStrategy":"type-guard","validationCode":"if (typeof handler !== 'function') throw new Error('handler must be a function');\nbox.addProcessor(/\\.md$/, handler);","typeGuard":"const isProcessorFn = (x: unknown): x is (...a: any[]) => any => typeof x === 'function';","tryCatchPattern":null,"preventionTips":["Type the handler parameter explicitly so the compiler rejects non-functions.","Log the imported symbol before registering to catch undefined imports.","Prefer the two-argument form (pattern, fn) to avoid argument-shift ambiguity."],"tags":["box","processor","validation","plugin","typescript"],"backgroundTag":null,"analyzedSha":"059cb17494d0632a053c24077f5bcb6ae92acc51","analyzedAt":"2026-08-12T20:52:05.731Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}