neoclide/coc.nvim · error

Unable to find parent marker in range ${JSON.stringify(range

Error message

Unable to find parent marker in range ${JSON.stringify(range, null, 2)}

What it means

findParent walks snippet markers to locate the marker containing a given range so edits can be applied with correct parent context. If no marker's range covers/associates with the requested range, the snippet structure and the edit range are inconsistent, so it throws. Called from result during snippet edit resolution.

Source

Thrown at src/snippets/snippet.ts:173

    })
    for (let index = list.length - 1; index >= 0; index--) {
      const o = list[index]
      if (rangeInRange(range, o.range)) {
        // Gives choice and final placeholder lower priority for text insert
        if (isInsert
          && o.marker instanceof Placeholder
          && (o.marker.choice || o.marker.index === 0)
          && o.marker !== current
          && adjacentPosition(range.start, o.range)
        ) {
          continue
        }
        marker = o.marker
        markerRange = o.range
        break
      }
    }
    if (!marker) throw new Error(`Unable to find parent marker in range ${JSON.stringify(range, null, 2)}`)
    return { marker, range: markerRange }
  }

  /**
   * The change must happens with same marker parents, return the changed marker
   */
  public replaceWithMarker(range: Range, marker: Marker, current?: Placeholder): Marker {
    // the range should already inside this.range
    const isInsert = emptyRange(range)
    const result = this.findParent(range, current)
    let parentMarker = result.marker
    let parentRange = result.range
    // search children need to be replaced
    const children = parentMarker.children
    let pos = parentRange.start
    let startIdx = 0
    let deleteCount = 0
    const { start, end } = range

View on GitHub (pinned to 50e974d969)

Solutions

  1. Recompute the range against the current snippet/buffer state.
  2. Ensure the range lies within the snippet's active region.
  3. Abort or restart the snippet session if the buffer changed underneath it.
  4. Use snippet manager APIs to obtain fresh markers instead of caching ranges.

Example fix

// before
let res = snippet.result(/** stale range */ range)
// after
let current = snippetManager.getCurrentSnippet() // revalidate against live state
if (!current || !contains(current.range, range)) return
let res = current.result(range)
Defensive patterns

Strategy: type-guard

Validate before calling

if (!snippet || !snippet.range) return
if (!containsRange(snippet.range, range)) return // range outside snippet

Type guard

function inSnippetRange(snippet: SnippetSession, range: Range): boolean {
  return snippet != null && range.start.line >= snippet.range.start.line && range.end.line <= snippet.range.end.line
}

Try / catch

try {
  let res = snippet.result(range)
} catch (e) {
  if (String(e.message).startsWith('Unable to find parent marker')) restartSnippetSession()
}

Prevention

When it happens

Trigger: Applying a TextEdit or calling snippet APIs with a range that does not fall inside any marker of the currently resolved snippet (e.g. stale offsets after buffer changed).

Common situations: Editing a snippet session after external buffer modifications shifted positions; computing ranges from old coordinates.

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/0056d007ffc3f8f5. Report an issue: GitHub.