microsoft/typescript-go · error
expected SourceFile root, got %v
Error message
expected SourceFile root, got %v
What it means
DecodeSourceFile decoded the binary buffer into a node tree, but the root node's syntax kind is not ast.KindSourceFile. The decoder always returns node index 1 as the root, so the buffer encodes a different AST shape, or corruption altered the kind field while still passing header validation.
Source
Thrown at internal/api/encoder/decoder.go:39
factory *ast.NodeFactory
childBuf []int
// Single Go string covering all string data; substrings are zero-alloc slices.
allStringData string
// Arena for batch-allocating []*ast.Node slices used by NodeLists.
nodeArena []*ast.Node
// Results
nodes []*ast.Node
nodeLists []*ast.NodeList
}
// DecodeSourceFile decodes binary-encoded data into an *ast.SourceFile.
func DecodeSourceFile(data []byte) (*ast.SourceFile, error) {
node, err := DecodeNodes(data)
if err != nil {
return nil, err
}
if node.Kind != ast.KindSourceFile {
return nil, fmt.Errorf("expected SourceFile root, got %v", node.Kind)
}
return node.AsSourceFile(), nil
}
// DecodeNodes decodes binary-encoded AST data into a tree of *ast.Node objects.
func DecodeNodes(data []byte) (*ast.Node, error) {
d, err := newASTDecoder(data)
if err != nil {
return nil, err
}
return d.decode()
}
func newASTDecoder(data []byte) (*astDecoder, error) {
if len(data) < HeaderSize {
return nil, fmt.Errorf("data too short for header: %d bytes", len(data))
}
version := data[HeaderOffsetMetadata+3]View on GitHub (pinned to 1bcfa18d79)
Solutions
- Confirm the blob was produced by encoding a whole SourceFile (same typescript-go version) and regenerate it
- If you encode arbitrary nodes, call DecodeNodes and switch on node.Kind instead of DecodeSourceFile
- Store an integrity hash next to cached blobs and verify before decoding
- Check for truncation by comparing the stored blob length against the encoder's output length
Example fix
// before
sf := encoder.DecodeSourceFile(data) // panics-ish on non-file roots
// after
n, err := encoder.DecodeNodes(data)
if err != nil { return err }
if n.Kind != ast.KindSourceFile { return fmt.Errorf("not a source file: %v", n.Kind) }
sf := n.AsSourceFile() Defensive patterns
Strategy: validation
Validate before calling
// Accept any encoded tree, then narrow to SourceFile yourself.
n, err := encoder.DecodeNodes(data)
if err != nil { return nil, err }
if n.Kind != ast.KindSourceFile {
return nil, fmt.Errorf("not a source file blob: root kind %v", n.Kind)
}
return n.AsSourceFile(), nil Type guard
func isSourceFileBlobRoot(n *ast.Node) bool { return n != nil && n.Kind == ast.KindSourceFile } Prevention
- Only store whole-file encodes under source-file cache keys
- Use DecodeNodes when handling arbitrary subtrees
- Hash cached blobs and verify before decoding
When it happens
Trigger: Feeding DecodeSourceFile data produced by encoding a non-SourceFile root via EncodeNodes; a truncated or garbled buffer whose header fields happen to validate; two encoded blobs concatenated or sliced wrongly.
Common situations: Caching layers that store arbitrary encoded subtrees under keys meant for whole files; partial writes of encoded artifacts; reusing one buffer for multiple purposes.
Related errors
- at node %d (kind %v): %w
- unsupported protocol version %d (expected %d)
- no nodes to decode
- data too short for header: %d bytes
- invalid AST header offsets: offsets exceed data length (%d)
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/82309c1309c660e3.
Report an issue: GitHub.