microsoft/typescript-go · error · ErrClientError
no nodes to decode
Error message
no nodes to decode
What it means
After the header, nodeCount is computed as (len(data)-nodeOff)/NodeSize; a count below 2 means no root exists (index 0 is reserved, the root lives at index 1). Valid encodings always contain at least the SourceFile root plus an EndOfFile token, so this is an empty or degenerate node region.
Source
Thrown at internal/api/encoder/decoder.go:140
if i+1 >= d.nodeCount {
return d.childBuf
}
firstChild := i + 1
if d.nodeField(firstChild, NodeOffsetParent) != uint32(i) {
return d.childBuf
}
d.childBuf = append(d.childBuf, firstChild)
next := int(d.nodeField(firstChild, NodeOffsetNext))
for next != 0 {
d.childBuf = append(d.childBuf, next)
next = int(d.nodeField(next, NodeOffsetNext))
}
return d.childBuf
}
func (d *astDecoder) decode() (*ast.Node, error) {
if d.nodeCount < 2 {
return nil, errors.New("no nodes to decode")
}
d.nodes = make([]*ast.Node, d.nodeCount)
d.nodeLists = make([]*ast.NodeList, d.nodeCount)
// Pre-allocate arena for NodeList child slices. Each node can appear as a
// child at most once, so nodeCount is an upper bound on total child pointers.
d.nodeArena = make([]*ast.Node, 0, d.nodeCount)
// Process bottom-up so children exist before parents.
for i := d.nodeCount - 1; i >= 1; i-- {
kind := d.nodeField(i, NodeOffsetKind)
pos := d.nodeField(i, NodeOffsetPos)
end := d.nodeField(i, NodeOffsetEnd)
data := d.nodeField(i, NodeOffsetData)
childIndices := d.collectChildren(i)
if kind == SyntaxKindNodeList {
childNodes := d.allocNodeSlice(len(childIndices))View on GitHub (pinned to 1bcfa18d79)
Solutions
- Ensure the blob came from a complete Encode call, not a partial/aborted write
- Re-encode from the parsed SourceFile
- Validate (len(data)-nodeOff)/NodeSize >= 2 before decoding untrusted blobs
- Check that nothing stripped the tail of the buffer (compare against the encoder's reported length)
Example fix
// before
decoder.DecodeSourceFile(trimmedBlob)
// after
if (len(blob)-int(nodeOff))/encoder.NodeSize < 2 {
return errors.New("empty AST blob")
} Defensive patterns
Strategy: validation
Validate before calling
nodeOff := int(binary.LittleEndian.Uint32(data[encoder.HeaderOffsetNodes:]))
if (len(data)-nodeOff)/encoder.NodeSize < 2 {
return errors.New("encoded AST has no nodes")
} Prevention
- Ensure encodes complete before publishing blobs
- Verify the node region exists when handling untrusted buffers
- Compare stored blob size with the encoder's reported size
When it happens
Trigger: A blob containing only the header with no node region; nodeOff == len(data); truncation that removes the node region while leaving header checks green.
Common situations: Header-only writes from a failed encode; slicing off the node region by accident; caches storing only the string table portion.
Related errors
- expected SourceFile root, got %v
- data too short for header: %d bytes
- unsupported protocol version %d (expected %d)
- invalid AST header offsets: offsets exceed data length (%d)
- invalid AST header offsets: expected strTable <= strData <=
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/d54554d34f8539a7.
Report an issue: GitHub.