{"record":{"id":"aae335deae8d0e8d","repo":"hexojs/hexo","slug":"fn-must-be-a-function-aae335","errorCode":null,"errorMessage":"fn must be a function","messagePattern":"fn must be a function","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/extend/filter.ts","lineNumber":50,"sourceCode":"  list(type?: string) {\n    if (!type) return this.store;\n    return this.store[type] || [];\n  }\n\n  register(fn: StoreFunction): void\n  register(fn: StoreFunction, priority: number): void\n  register(type: string, fn: StoreFunction): void\n  register(type: string, fn: StoreFunction, priority: number): void\n  register(type: string | StoreFunction, fn?: StoreFunction | number, priority?: number): void {\n    if (!priority) {\n      if (typeof type === 'function') {\n        priority = fn as number;\n        fn = type;\n        type = 'after_post_render';\n      }\n    }\n\n    if (typeof fn !== 'function') throw new TypeError('fn must be a function');\n\n    type = typeAlias[type as string] || type;\n    priority = priority == null ? 10 : priority;\n\n    const store = this.store[type as string] || [];\n    this.store[type as string] = store;\n\n    fn.priority = priority;\n    store.push(fn);\n\n    store.sort((a, b) => a.priority - b.priority);\n  }\n\n  unregister(type: string, fn: StoreFunction): void {\n    if (!type) throw new TypeError('type is required');\n    if (typeof fn !== 'function') throw new TypeError('fn must be a function');\n\n    type = typeAlias[type] || type;","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/hexojs/hexo/blob/059cb17494d0632a053c24077f5bcb6ae92acc51/lib/extend/filter.ts#L32-L68","documentation":"Thrown by Filter.register when, after argument shifting, 'fn' is not a function. Hexo filters support (fn,priority), (type,fn), and (type,fn,priority); when the first arg is a function it is promoted to fn. If after that resolution fn is still not callable, the registration is invalid.","triggerScenarios":"Calling hexo.extend.filter.register('before_post_render', 'notafn'), register('type', undefined), or register('type', 123) where the second argument is not a function.","commonSituations":"A plugin passes a config string/object instead of a filter callback, or imports a filter that is undefined due to a bad import path.","solutions":["Pass a function as the filter callback in the correct positional slot.","Verify imported filter symbols are defined before registering.","Match the overload: register(type, fn) or register(type, fn, priority)."],"exampleFix":"// before\nhexo.extend.filter.register('before_post_render', filterDef);\n// after\nhexo.extend.filter.register('before_post_render', (data) => { /* ... */ return data; });","handlingStrategy":"type-guard","validationCode":"if (typeof fn !== 'function') throw new Error('filter callback must be a function');\nhexo.extend.filter.register(type, fn, priority);","typeGuard":"const isFilterFn = (x: unknown): x is (...a: any[]) => any => typeof x === 'function';","tryCatchPattern":null,"preventionTips":["Always pass filter callbacks as the function slot in the (type, fn[, priority]) overload.","Verify imported filter symbols are defined.","Add type annotations to filter registration helpers."],"tags":["filter","validation","plugin","typescript"],"backgroundTag":null,"analyzedSha":"059cb17494d0632a053c24077f5bcb6ae92acc51","analyzedAt":"2026-08-12T20:52:05.731Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}