Skip to content

Conversation

@nanov94
Copy link

@nanov94 nanov94 commented Nov 13, 2024

What?

Get error: TypeError: Cannot read properties of undefined (reading 'nodeType')

Why?

In some cases node does not have children and as a result we get property nodeType from undefined

How?

Checked existing of prev

@marijnh
Copy link
Member

marijnh commented Nov 13, 2024

The only way this seems to be possible is if offset is either negative or pointing beyond the length of the node. Both would suggest something is going wrong before this point, and I don't like adding tests like this to paper over other bugs. So I'd be interested in an example of a situation that triggers this crash.

@nanov94
Copy link
Author

nanov94 commented Nov 21, 2024

Hi @marijnh :)
Thank you for your comment 🙏

Summary:

We are encountering an error (below) when selecting table cells, particularly when the table contains empty rows.
Error: TypeError: Cannot read properties of undefined (reading 'nodeType')

More details:

Yes, you are correct about the case where offset is either negative or points beyond the length of the node. In my app, we have another case where offset is 0 and node has no children.

Why is this the case? Empty rows without table cells:
Firstly, a tr element in HTML can have empty content here. This mean that node.childNodes is empty array:

  1. node does not have any children => node.childNodes = []
  2. we got element by index in empty array => node.childNodes[offset - 1] = undefined
  3. after got field from undefined element => node.childNodes[offset - 1]).nodeType

Secondly, this happened after using another ProseMirror library: prosemirror-tables.
How can we create tables with empty rows via ProseMirror? You need to select a few table cells and rows and merge them. We use mergeCells from prosemirror-tables for it (video attached)
Demo merge table.webm

@marijnh
Copy link
Member

marijnh commented Nov 21, 2024

where offset is 0 and node has no children.

The line already contains && offset, which will be false when offset == 0, so the code that could cause this crash will simply not execute in that situation.

@nanov94
Copy link
Author

nanov94 commented Nov 25, 2024

The line already contains && offset, which will be false when offset == 0, so the code that could cause this crash will simply not execute in that situation.

Yep, @marijnh, you are right, sorry for confusing 🙏

We catch error when offset more than 0 and node.childNodes does not have children

image

@marijnh
Copy link
Member

marijnh commented Nov 25, 2024

What version of prosemirror-view are you using? In anything older than 1.31.0 there might be a situation where this happens, on Chrome when the document contains an <input> or <textarea> element.

@nanov94
Copy link
Author

nanov94 commented Nov 25, 2024

we use "prosemirror-view": "1.33.5",

@marijnh
Copy link
Member

marijnh commented Nov 25, 2024

In that case, I'd really want to see instructions on how to reproduce this error, so that I can figure out what the underlying issue is.

@maccman
Copy link

maccman commented Aug 28, 2025

We are also seeing this error in Refect Notes (reflect.app). I'm sorry I don't know how to reproduce it though. We're just seeing it in our error logs.

@maccman
Copy link

maccman commented Aug 28, 2025

This is what GPT5 is saying:

  • The error is thrown when prev ends up undefined and the code still tries to read prev.nodeType.
  • This can happen in WebKit/Chromium when caret*FromPoint returns an out-of-bounds offset (e.g., offset > node.childNodes.length or offset == 1 on an empty element). Then node.childNodes[offset - 1] is undefined.

The problematic spot is here:

    let prev
    // When clicking above the right side of an uneditable node, Chrome will report a cursor position after that node.
    if (browser.webkit && offset && node.nodeType == 1 && (prev = node.childNodes[offset - 1]).nodeType == 1 &&
        (prev as HTMLElement).contentEditable == "false" && (prev as HTMLElement).getBoundingClientRect().top >= coords.top)
      offset--
    // Suspiciously specific kludge to work around caret*FromPoint
    // never returning a position at the end of the document
    if (node == view.dom && offset == node.childNodes.length - 1 && node.lastChild!.nodeType == 1 &&

Two minimal fixes (either is fine; you can also do both):

  • Add a null-check before accessing nodeType:

    if (browser.webkit && offset && node.nodeType == 1 &&
        (prev = node.childNodes[offset - 1]) && (prev as Node).nodeType == 1 &&
        (prev as HTMLElement).contentEditable == "false" &&
        (prev as HTMLElement).getBoundingClientRect().top >= coords.top) {
      offset--
    }
  • Clamp offset to the valid range before indexing:

    if (node.nodeType == 1) {
      offset = Math.max(0, Math.min(offset, node.childNodes.length))
    }

Note there’s a second potential out-of-bounds read a few lines below:

else if (offset == 0 || node.nodeType != 1 || node.childNodes[offset - 1].nodeName != "BR")

Clamping offset (as above) will prevent that too.

  • Impact: Prevents TypeError when browsers return out-of-range offsets for empty or special elements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants