microsoft/typescript-go · error · ErrClientError

at node %d (kind %v): %w

Error message

at node %d (kind %v): %w

What it means

Wrapper error from the bottom-up node reconstruction loop: node i (index and syntax kind are printed) failed in createNode. The wrapped error comes from the generated tables - 'unknown string node kind', 'unknown extended data node kind', or 'unhandled node kind with N children' - meaning the kind/data bytes map to no constructor this build knows.

Source

Thrown at internal/api/encoder/decoder.go:172

		data := d.nodeField(i, NodeOffsetData)
		childIndices := d.collectChildren(i)

		if kind == SyntaxKindNodeList {
			childNodes := d.allocNodeSlice(len(childIndices))
			for _, ci := range childIndices {
				if d.nodes[ci] != nil {
					childNodes = append(childNodes, d.nodes[ci])
				}
			}
			nl := d.factory.NewNodeList(childNodes)
			nl.Loc = core.NewTextRange(int(pos), int(end))
			d.nodeLists[i] = nl
			continue
		}

		node, err := d.createNode(ast.Kind(kind), data, childIndices)
		if err != nil {
			return nil, fmt.Errorf("at node %d (kind %v): %w", i, ast.Kind(kind), err)
		}
		node.Loc = core.NewTextRange(int(pos), int(end))
		node.Flags = ast.NodeFlags(d.nodeField(i, NodeOffsetFlags))
		d.nodes[i] = node
	}

	return d.nodes[1], nil
}

// getModifierList creates a *ast.ModifierList from a child index that is a NodeList.
func (d *astDecoder) getModifierList(ci int) *ast.ModifierList {
	nl := d.nodeLists[ci]
	if nl == nil {
		return nil
	}
	ml := d.factory.NewModifierList(nl.Nodes)
	ml.Loc = nl.Loc
	return ml

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Pin encoder and decoder to the exact same typescript-go build and re-encode
  2. Regenerate stored fixtures/caches after any upgrade
  3. Use the printed node index and kind diagnostically: one wildly large kind means corruption; many failures across kinds means version skew
  4. Add integrity verification before decoding blobs from untrusted sources

Example fix

// before
client := newClient(serverV6) // encodes with newer kind table
server := newServer(libV5)     // decodes with older table

// after
client := newClient(libV5)
server := newServer(libV5) // same build on both ends
Defensive patterns

Strategy: try-catch

Try / catch

sf, err := encoder.DecodeSourceFile(data)
if err != nil {
	var idx int; var kind ast.Kind
	if _, e := fmt.Sscanf(err.Error(), "at node %d (kind %v)", &idx, &kind); e == nil {
		log.Printf("blob incompatible or corrupt at node %d (kind %v); re-encoding", idx, kind)
		return reparseAndEncode(source)
	}
	return nil, err
}

Prevention

When it happens

Trigger: Version skew where the encoder emitted a syntax kind or data-type tag this decoder's generated tables don't cover; bit-flipped kind fields in a corrupted blob; truncation making the last node's fields read as garbage.

Common situations: Upgrading one side of a pair that exchanges encoded ASTs; long-lived caches surviving an upgrade; fuzzed or checksum-less transport of blobs.

Related errors


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