hexojs/hexo · error · TypeError
fn must be a function
Error message
fn must be a function
What it means
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.
Source
Thrown at lib/extend/filter.ts:50
list(type?: string) {
if (!type) return this.store;
return this.store[type] || [];
}
register(fn: StoreFunction): void
register(fn: StoreFunction, priority: number): void
register(type: string, fn: StoreFunction): void
register(type: string, fn: StoreFunction, priority: number): void
register(type: string | StoreFunction, fn?: StoreFunction | number, priority?: number): void {
if (!priority) {
if (typeof type === 'function') {
priority = fn as number;
fn = type;
type = 'after_post_render';
}
}
if (typeof fn !== 'function') throw new TypeError('fn must be a function');
type = typeAlias[type as string] || type;
priority = priority == null ? 10 : priority;
const store = this.store[type as string] || [];
this.store[type as string] = store;
fn.priority = priority;
store.push(fn);
store.sort((a, b) => a.priority - b.priority);
}
unregister(type: string, fn: StoreFunction): void {
if (!type) throw new TypeError('type is required');
if (typeof fn !== 'function') throw new TypeError('fn must be a function');
type = typeAlias[type] || type;View on GitHub (pinned to 059cb17494)
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).
Example fix
// before
hexo.extend.filter.register('before_post_render', filterDef);
// after
hexo.extend.filter.register('before_post_render', (data) => { /* ... */ return data; }); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof fn !== 'function') throw new Error('filter callback must be a function');
hexo.extend.filter.register(type, fn, priority); Type guard
const isFilterFn = (x: unknown): x is (...a: any[]) => any => typeof x === 'function';
Prevention
- 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.
When it happens
Trigger: Calling hexo.extend.filter.register('before_post_render', 'notafn'), register('type', undefined), or register('type', 123) where the second argument is not a function.
Common situations: 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.
Related errors
- fn must be a function
- type is required
- fn must be a function
- fn must be a function
- fn must be a function
AI-assisted analysis of hexojs/hexo@059cb17494 (2026-08-12).
Data as JSON: /api/errors/aae335deae8d0e8d.
Report an issue: GitHub.