{"record":{"id":"4b2b5b44a9a53db4","repo":"microsoft/typescript-go","slug":"data-too-short-for-header-d-bytes","errorCode":null,"errorMessage":"data too short for header: %d bytes","messagePattern":"data too short for header: (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/api/encoder/decoder.go","lineNumber":55,"sourceCode":"\t}\n\tif node.Kind != ast.KindSourceFile {\n\t\treturn nil, fmt.Errorf(\"expected SourceFile root, got %v\", node.Kind)\n\t}\n\treturn node.AsSourceFile(), nil\n}\n\n// DecodeNodes decodes binary-encoded AST data into a tree of *ast.Node objects.\nfunc DecodeNodes(data []byte) (*ast.Node, error) {\n\td, err := newASTDecoder(data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn d.decode()\n}\n\nfunc newASTDecoder(data []byte) (*astDecoder, error) {\n\tif len(data) < HeaderSize {\n\t\treturn nil, fmt.Errorf(\"data too short for header: %d bytes\", len(data))\n\t}\n\tversion := data[HeaderOffsetMetadata+3]\n\tif version != ProtocolVersion {\n\t\treturn nil, fmt.Errorf(\"unsupported protocol version %d (expected %d)\", version, ProtocolVersion)\n\t}\n\n\tstrTable := readLE32(data, HeaderOffsetStringOffsets)\n\tstrData := readLE32(data, HeaderOffsetStringData)\n\textData := readLE32(data, HeaderOffsetExtendedData)\n\tnodeOff := readLE32(data, HeaderOffsetNodes)\n\n\tdataLen := uint32(len(data))\n\n\t// Validate that all offsets are within the buffer.\n\tif strTable > dataLen || strData > dataLen || extData > dataLen || nodeOff > dataLen {\n\t\treturn nil, fmt.Errorf(\"invalid AST header offsets: offsets exceed data length (%d)\", dataLen)\n\t}\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/api/encoder/decoder.go#L37-L73","documentation":"The binary AST decoder requires at least HeaderSize bytes of fixed header before anything can be read; the supplied buffer is shorter. This is the first sanity gate in newASTDecoder and fires before version or offset checks.","triggerScenarios":"Passing an empty or near-empty slice; passing base64 text instead of decoded bytes; passing raw TypeScript source; a truncated cache file or a zero-length read from transport.","commonSituations":"Forgetting base64 decode before DecodeSourceFile (the API layer decodes base64 handles itself); cache files truncated by disk-full or interrupted writes; slicing a larger buffer with wrong indices.","solutions":["If the data arrived as base64 (as in API node handles), decode it with base64.StdEncoding first","Guard with len(data) >= encoder.HeaderSize before calling and reject upstream","Re-encode the source file to regenerate a valid blob","Verify the transport preserved payload length (content-length or framing mismatch)"],"exampleFix":"// before\nnode, err := encoder.DecodeSourceFile([]byte(b64String))\n\n// after\nraw, err := base64.StdEncoding.DecodeString(b64String)\nif err != nil { return err }\nnode, err := encoder.DecodeSourceFile(raw)","handlingStrategy":"validation","validationCode":"if len(data) < encoder.HeaderSize {\n\treturn fmt.Errorf(\"encoded AST too short: %d bytes (need %d)\", len(data), encoder.HeaderSize)\n}\nsf, err := encoder.DecodeSourceFile(data)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Base64-decode API payloads before DecodeSourceFile","Check lengths on cache reads and reject short blobs upstream","Verify transport framing preserves payload length"],"tags":["go","encoding","binary","validation","truncation","base64"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}