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 } = rangeView on GitHub (pinned to 50e974d969)
Solutions
- Recompute the range against the current snippet/buffer state.
- Ensure the range lies within the snippet's active region.
- Abort or restart the snippet session if the buffer changed underneath it.
- 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
- Never cache marker ranges across buffer changes.
- Recompute ranges immediately before edit operations.
- Abort snippet sessions when external edits land inside them.
- Use session APIs instead of raw offsets.
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
- `range` cannot span multiple lines
- Illegal value for `line`
- Parent placeholder has same index: ${idx}
- pattern \z not supported
- pattern (?s) not supported
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/0056d007ffc3f8f5.
Report an issue: GitHub.