denoland/deno · error

Failed to convert range to tsc text span

Error message

Failed to convert range to tsc text span: {:#}

What it means

Inlay hint requests first translate the client's LSP range into a tsc TextSpan using the module's line index; this wraps a failure of that translation (tsc::TextSpan::from_range). It means the requested range does not map into the current document content — classically because the document changed between the client building the request and the server processing it, leaving stale line/character positions. The adjacent code separately silences tsc 'Debug Failure' responses, showing this path is known to be timing-sensitive.

Solutions

  1. Let edits settle or save — the next inlay hints request re-syncs the range and usually succeeds.
  2. Retry the request; this failure is nearly always transient.
  3. Run "Deno: Restart Language Server" if hints stay broken for one file.
  4. Update Deno — inlay-hint handling around tsc failures has required patches before (see the adjacent Debug Failure workaround referencing denoland/deno#30455).
Defensive patterns

Strategy: validation

Validate before calling

// Before sending textDocument/inlayHint, clamp the range to the document's
// current extent so TextSpan::from_range can always map it. In VS Code the
// built-in TextDocument.validateRange() does exactly this.
function clampRangeToDocument(range, lineCount, lineAt) {
  const lastLine = lineCount - 1;
  const clamp = (pos) => {
    const line = Math.min(Math.max(pos.line, 0), lastLine);
    const maxChar = line === lastLine ? lineAt(lastLine).length : Number.MAX_SAFE_INTEGER;
    return { line, character: Math.min(Math.max(pos.character, 0), maxChar) };
  };
  return { start: clamp(range.start), end: clamp(range.end) };
}

Try / catch

// If a hint request still fails server-side, re-request after the next
// didChange/didSave flush rather than showing an error.
try {
  hints = await client.sendRequest('textDocument/inlayHint', { range, textDocument });
} catch (err) {
  if (String(err?.message).includes('tsc text span')) {
    hints = null; // next natural refresh (save/scroll) re-syncs the range
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: A textDocument/inlayHint request whose range was computed against an older document version — hints requested immediately after edits or during scroll-triggered refreshes — so offset conversion for the range start or end fails against the current line index.

Common situations: Typing or scrolling with inlay hints enabled while the editor re-requests hints for the visible range; intermittent during rapid editing; typically self-healing on the next request.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/05dc1e30d5038d95. Report an issue: GitHub.

Appendix: source

Thrown at cli/lsp/ts_server.rs:1211

          &snapshot,
          token,
        )
      }
    }
  }

  pub async fn provide_inlay_hint(
    &self,
    module: &DocumentModule,
    range: lsp::Range,
    snapshot: &Arc<StateSnapshot>,
    token: &CancellationToken,
  ) -> Result<Option<Vec<lsp::InlayHint>>, AnyError> {
    match self {
      Self::Js(ts_server) => {
        let text_span = tsc::TextSpan::from_range(range, &module.line_index)
          .map_err(|err| {
            anyhow!("Failed to convert range to tsc text span: {:#}", err)
          })?;
        let mut inlay_hints = ts_server
          .provide_inlay_hints(snapshot.clone(), module, text_span, token)
          .await;
        // Silence tsc debug failures.
        // See https://github.com/denoland/deno/issues/30455.
        // TODO(nayeemrmn): Keeps tabs on whether this is still necessary.
        if let Err(err) = &inlay_hints
          && err.to_string().contains("Debug Failure")
        {
          lsp_warn!("Unable to get inlay hints from TypeScript: {:#}", err);
          inlay_hints = Ok(None)
        }
        inlay_hints?
          .map(|inlay_hints| {
            inlay_hints
              .into_iter()
              .map(|inlay_hint| {

View on GitHub (pinned to 9ad36f7a2c)