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.style

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Pass the function reference, not its return value: `onClick={handleClick}`
  2. Fix conditional handlers to resolve to a function or undefined/null (`onClick={enabled ? handler : undefined}`)
  3. 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

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


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/0302d6dc066fba18. Report an issue: GitHub.