{"record":{"id":"8cb0f690796ee1d7","repo":"v2rayA/v2rayA","slug":"read-frame-body-w","errorCode":null,"errorMessage":"read frame body: %w","messagePattern":"read frame body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/main/geosite.go","lineNumber":144,"sourceCode":"\t\t}\n\t\tif tagByte != 0x0a {\n\t\t\t// Not the expected frame format — skip.\n\t\t\tcontinue\n\t\t}\n\n\t\t// Read the varint length of the message body.\n\t\tbodyLen, err := binary.ReadUvarint(br)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"read frame length: %w\", err)\n\t\t}\n\n\t\t// Read the message body.\n\t\tbody := make([]byte, bodyLen)\n\t\tif _, err := io.ReadFull(br, body); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"read frame body: %w\", err)\n\t\t}\n\n\t\t// Unmarshal as GeoSite and check code.\n\t\tvar site geodata.GeoSite\n\t\tif err := proto.Unmarshal(body, &site); err != nil {\n\t\t\t// Skip entries we can't parse.\n\t\t\tcontinue\n\t\t}\n\n\t\tif site.Code != tag && site.Code != tagUpper {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Found matching entry — cache and return.\n\t\tdomains := site.Domain\n\t\tgeositeCache.mu.Lock()\n\t\tgeositeCache.data[cacheKey] = domains\n\t\tgeositeCache.mu.Unlock()","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/v2rayA/v2rayA/blob/71e5442fc548c05680ee55eae943e6c0afe9ac51/core/main/geosite.go#L126-L162","documentation":"While scanning a .dat geosite file frame-by-frame, io.ReadFull failed to read the announced bodyLen bytes of a frame body. The file is either truncated/corrupt or the framing is wrong, so loadGeositeTag aborts reading this file.","triggerScenarios":"io.ReadFull(br, body) returns fewer than bodyLen bytes: the geosite .dat file was truncated during download, is corrupt on disk, or the declared frame length exceeds remaining file data.","commonSituations":"Interrupted download of geosite.dat left a partial file; disk corruption; a custom-built geosite.dat produced with a mismatched protobuf/framing version; mounted file cut short in a container image.","solutions":["Re-download / replace the geosite .dat file with a fresh, complete copy","Verify file size and checksum against the official release","Rebuild the .dat from source with a matching geodata protocol version","Check the configured geosite path points to the intended file, not a truncated leftover"],"exampleFix":"// before\nbody := make([]byte, bodyLen)\nif _, err := io.ReadFull(br, body); err != nil {\n\treturn nil, fmt.Errorf(\"read frame body: %w\", err)\n}\n// after\n// redeploy a complete geosite.dat first, e.g.:\n// curl -fLo geosite.dat https://example.com/geosite.dat && sha256sum -c geosite.dat.sha256\nbody := make([]byte, bodyLen)\nif _, err := io.ReadFull(br, body); err != nil {\n\treturn nil, fmt.Errorf(\"read frame body (file may be truncated/corrupt): %w\", err)\n}","handlingStrategy":"validation","validationCode":"// verify the geosite file before loading\nst, err := os.Stat(geositePath)\nif err != nil || st.Size() < 1024 { return fmt.Errorf(\"geosite file missing or truncated: %s\", geositePath) }\n// optional: verify sha256 against the release checksum","typeGuard":null,"tryCatchPattern":"tags, err := loadGeositeTag(path, tag)\nif err != nil {\n\tif strings.Contains(err.Error(), \"read frame\") {\n\t\t// file corrupt: re-download before giving up\n\t}\n\treturn nil, err\n}","preventionTips":["Verify checksums of geosite.dat after download","Download atomically (tmp file + rename) to avoid partial files","Pin a known-good geosite.dat version in deployments","Handle read errors by re-fetching the data file automatically"],"tags":["geosite","file-io","corruption","truncated-file"],"backgroundTag":"corrupt-data-file","analyzedSha":"71e5442fc548c05680ee55eae943e6c0afe9ac51","analyzedAt":"2026-09-05T20:04:37.459Z","contentChangedAt":"2026-09-05T20:04:37.459Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}