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

  1. Confirm the blob was produced by encoding a whole SourceFile (same typescript-go version) and regenerate it
  2. If you encode arbitrary nodes, call DecodeNodes and switch on node.Kind instead of DecodeSourceFile
  3. Store an integrity hash next to cached blobs and verify before decoding
  4. 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

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


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/82309c1309c660e3. Report an issue: GitHub.