ianstormtaylor/slate · error · Error

Failed to attach MutationObserver, `node` is undefined

Error message

Failed to attach MutationObserver, `node` is undefined

What it means

useMutationObserver (used internally by slate-react's Android input manager) attaches a MutationObserver to a ref's current node in an effect. If node.current is still undefined when the effect runs, the observer cannot be attached, so the hook throws. This typically means the ref was never attached to a rendered DOM node or the node unmounted before the effect.

Source

Thrown at packages/slate-react/src/hooks/use-mutation-observer.ts:19

import { RefObject, useEffect, useState } from 'react'
import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect'

export function useMutationObserver(
  node: RefObject<HTMLElement>,
  callback: MutationCallback,
  options: MutationObserverInit
) {
  const [mutationObserver] = useState(() => new MutationObserver(callback))

  useIsomorphicLayoutEffect(() => {
    // Discard mutations caused during render phase. This works due to react calling
    // useLayoutEffect synchronously after the render phase before the next tick.
    mutationObserver.takeRecords()
  })

  useEffect(() => {
    if (!node.current) {
      throw new Error('Failed to attach MutationObserver, `node` is undefined')
    }

    mutationObserver.observe(node.current, options)
    return () => mutationObserver.disconnect()
  }, [mutationObserver, node, options])
}

View on GitHub (pinned to 72a37c701e)

Solutions

  1. Ensure the ref is attached to a DOM element that is always rendered before the effect runs (forward the ref through any wrapper components)
  2. If the node renders conditionally, guard the hook usage so the observer component only mounts when the node exists
  3. Verify you're not calling the hook during SSR — only attach after mount

Example fix

// before
const ref = useRef<HTMLDivElement>(null)
useMutationObserver(ref, callback, options) // ref.current still null
return cond && <div ref={ref} />

// after
return <div ref={ref}>{cond && <Child />}</div>
// node always mounted; observer attaches safely
Defensive patterns

Strategy: validation

Validate before calling

if (node.current != null) {
  useMutationObserverEffect(node, cb, options)
}

Type guard

const isRefAttached = (ref) => ref.current != null

Prevention

When it happens

Trigger: Passing a ref object to useMutationObserver whose .current is null/undefined at effect time (node not yet mounted, component returned null, or the ref was never forwarded to a DOM element); SSR where refs never populate.

Common situations: Using useAndroidInputManager or useMutationObserver with a conditionally-rendered element; forgetting to forward the ref in a wrapped component; React 18 strict-mode timing changes on Android-focused code paths.

Related errors


AI-assisted analysis of ianstormtaylor/slate@72a37c701e (2026-08-27). Data as JSON: /api/errors/850a549fabc5073d. Report an issue: GitHub.