honojs/hono · error · Error
Event handler for "${key}" is not a function
Error message
Event handler for "${key}" is not a function What it means
When hono/jsx/dom applies event-handler props (names starting with 'on') to DOM nodes, the value must be a function so it can be registered with addEventListener. If the prop is defined, non-null, and not a function, applyProps throws during render/diffing.
Source
Thrown at src/jsx/dom/render.ts:182
const applyProps = (
container: SupportedElement,
attributes: Props,
oldAttributes?: Props
): void => {
attributes ||= {}
for (let key in attributes) {
const value = attributes[key]
if (key !== 'children' && (!oldAttributes || oldAttributes[key] !== value)) {
key = normalizeIntrinsicElementKey(key)
const eventSpec = getEventSpec(key)
if (eventSpec) {
if (oldAttributes?.[key] !== value) {
if (oldAttributes) {
container.removeEventListener(eventSpec[0], oldAttributes[key], eventSpec[1])
}
if (value != null) {
if (typeof value !== 'function') {
throw new Error(`Event handler for "${key}" is not a function`)
}
container.addEventListener(eventSpec[0], value, eventSpec[1])
}
}
} else if (key === 'dangerouslySetInnerHTML' && value) {
container.innerHTML = value.__html
} else if (key === 'ref') {
refCleanupMap.get(container)?.()
let cleanup
if (typeof value === 'function') {
cleanup = value(container) || (() => value(null))
} else if (value && 'current' in value) {
value.current = container
cleanup = () => (value.current = null)
}
refCleanupMap.set(container, cleanup)
} else if (key === 'style') {
const style = container.styleView on GitHub (pinned to e2740d5a1b)
Solutions
- Pass the function reference, not its return value: `onClick={handleClick}`
- Fix conditional handlers to resolve to a function or undefined/null (`onClick={enabled ? handler : undefined}`)
- When spreading props, filter out or correct any on* keys that hold non-function values
Example fix
// before
<button onClick={handleClick()}>Save</button>
// or
<button onClick={mode === 'edit' && 'handleSave'}>Save</button>
// after
<button onClick={handleClick}>Save</button>
// or
<button onClick={mode === 'edit' ? handleSave : undefined}>Save</button> Defensive patterns
Strategy: type-guard
Validate before calling
const handler = typeof props.onClick === 'function' ? props.onClick : undefined
<button onClick={handler}>Save</button> Type guard
const isEventHandler = (v: unknown): v is (e: Event) => void => typeof v === 'function'
Try / catch
null
Prevention
- Pass handler references, never invocations
- Make conditional handlers resolve to a function or undefined
- Sanitize spread props so on* keys are functions or absent
When it happens
Trigger: Passing a non-function to an on* prop: `<button onClick={'clicked'}>`, `<div onMouseEnter={flag && handler}>` where the expression evaluates to a truthy non-function, or spreading an object with an on* key whose value is a string/number.
Common situations: Passing the result of invoking a handler instead of the reference (`onClick={handleClick()}`); conditional handlers using `||`/`&&` that leak truthy non-function values; spreading arbitrary props objects onto DOM elements; data mistakenly placed under an on* key.
Related errors
- Invalid prop '${key}' of type 'function' supplied to '${tag}
- Cannot update an unmounted root
- style sheet not found
- Invalid JSX tag name: ${tag}
- Can only set one of `children` or `props.dangerouslySetInner
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/0302d6dc066fba18.
Report an issue: GitHub.