microsoft/typescript-go · error
Invalid node handle: ${handle}
Error message
Invalid node handle: ${handle} What it means
Thrown by parseNodeHandle when the handle string contains no '.' at all. Node handles have the format "index.kind.path" (path may itself contain dots); the first indexOf('.') failing means the input is a single segment - e.g., a bare numeric id, a filename, or some other identifier that is not a handle.
Source
Thrown at _packages/native-preview/src/api/node/node.ts:328
}
/**
* Parsed components of a node handle.
*/
export interface ParsedNodeHandle {
index: number;
kind: SyntaxKind;
path: Path;
}
/**
* Parse a node handle string into its components.
* Handle format: "index.kind.path" where path may contain dots.
*/
export function parseNodeHandle(handle: string): ParsedNodeHandle {
const firstDot = handle.indexOf(".");
if (firstDot === -1) {
throw new Error(`Invalid node handle: ${handle}`);
}
const secondDot = handle.indexOf(".", firstDot + 1);
if (secondDot === -1) {
throw new Error(`Invalid node handle: ${handle}`);
}
return {
index: parseInt(handle.slice(0, firstDot), 10),
kind: parseInt(handle.slice(firstDot + 1, secondDot), 10) as SyntaxKind,
path: handle.slice(secondDot + 1) as Path,
};
}
/**
* Decode binary-encoded AST data into a Node.
* Works for any binary-encoded node, including synthetic nodes
* (e.g. from typeToTypeNode) that don't have a source file.
*/
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Only pass handles obtained from RemoteNode.id / API responses - never construct them by hand
- Validate the shape first (must contain at least two '.') and reject early with your own error message
- If you only have an index, use the appropriate API (node lookup by index) instead of forging a handle
- Check for truncation where the handle was stored/transported and its tail was cut off
Example fix
// before
const parsed = parseNodeHandle(userInput); // e.g. '42' or 'main.ts' -> throws
// after
const NODE_HANDLE_RE = /^\d+\.\d+\..+$/;
if (!NODE_HANDLE_RE.test(userInput)) {
throw new TypeError(`Not a node handle: ${userInput}`);
}
const parsed = parseNodeHandle(userInput); Defensive patterns
Strategy: type-guard
Validate before calling
// Require the full 'index.kind.path' shape before parsing
const NODE_HANDLE_RE = /^\d+\.\d+\..+$/;
if (!NODE_HANDLE_RE.test(handle)) {
throw new TypeError(`Not a node handle (expected 'index.kind.path'): ${handle}`);
}
const parsed = parseNodeHandle(handle); Type guard
const isNodeHandle = (s: string): boolean => /^\d+\.\d+\..+$/.test(s);
Try / catch
try {
parsed = parseNodeHandle(value);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid node handle')) {
// wrong kind of identifier passed: fail with context instead of propagating
throw new TypeError(`Expected a node handle from RemoteNode.id, got: ${value}`);
}
throw e;
} Prevention
- Treat node handles as opaque API-produced strings - never synthesize them
- Keep symbol ids, file paths, and node handles in distinct typed variables
- Validate handle shape at deserialization boundaries (storage, IPC, logs)
When it happens
Trigger: Passing a raw file path, symbol name, numeric node index, or arbitrary user string where a node handle is expected; handles truncated to their first segment; strings sourced from a different tool with its own id format.
Common situations: Plumbing tsgo API ids (symbol ids, project ids) into functions that expect node handles; debug tooling that reconstructs handles by string concatenation and drops segments; deserialization bugs stripping dotted suffixes.
Related errors
- Cannot run a temporary file update on an inactive snapshot
- Snapshot is disposed
- Connection not established
- Cannot create directory: a file already exists at "/${segmen
- Invalid file path: "${path}"
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/da232530d6d888e3.
Report an issue: GitHub.