{"record":{"id":"79ab64f3ed47ea7f","repo":"fish2018/pansou","slug":"error-79ab64","errorCode":null,"errorMessage":"转义序列不完整","messagePattern":"转义序列不完整","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugin/haitunsou/haitunsou.go","lineNumber":254,"sourceCode":"\t\t\tif r >= 0xD800 && r <= 0xDBFF && next+6 <= len(encoded) && encoded[next] == '\\\\' && encoded[next+1] == 'u' {\n\t\t\t\tlow, lowNext, lowErr := decodeHexEscape(encoded, next+2, 4)\n\t\t\t\tif lowErr == nil && low >= 0xDC00 && low <= 0xDFFF {\n\t\t\t\t\tr = utf16.DecodeRune(r, rune(low))\n\t\t\t\t\tindex = lowNext - 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tdecoded = utf8.AppendRune(decoded, r)\n\t\tdefault:\n\t\t\tdecoded = append(decoded, encoded[index])\n\t\t}\n\t}\n\treturn decoded, nil\n}\n\nfunc decodeHexEscape(data []byte, start, length int) (uint64, int, error) {\n\tend := start + length\n\tif start < 0 || end > len(data) {\n\t\treturn 0, start, fmt.Errorf(\"转义序列不完整\")\n\t}\n\tvalue, err := strconv.ParseUint(string(data[start:end]), 16, 16)\n\tif err != nil {\n\t\treturn 0, start, fmt.Errorf(\"无效十六进制转义: %w\", err)\n\t}\n\treturn value, end, nil\n}\n\nfunc convertItem(item searchItem) (model.SearchResult, bool) {\n\trawTitle := cleanText(item.Title)\n\tif rawTitle == \"\" {\n\t\trawTitle = cleanText(item.Name)\n\t}\n\tif rawTitle == \"\" {\n\t\treturn model.SearchResult{}, false\n\t}\n\ttitle, description := splitTitleAndDescription(rawTitle)\n\tif title == \"\" {","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/haitunsou/haitunsou.go#L236-L272","documentation":"decodeHexEscape parses a fixed-length run of hex digits from a JS single-quoted string's \\xNN escape in the haitunsou plugin. It throws 转义序列不完整 (incomplete escape sequence) when the computed slice [start, start+length) extends past the input data, meaning the escape started but not enough bytes remain to complete it (e.g. a truncated \\x at end of input).","triggerScenarios":"Parsing a JS string where a \\x escape (or \\u escape feeding decodeHexEscape) appears with fewer than `length` hex-capable bytes left: data ends immediately after \\x, or after \\x with only one hex digit.","commonSituations":"Scraping haitunsou pages whose embedded JavaScript was truncated by the upstream site, cut off by a proxy/CDN, or whose encoder emitted a malformed \\x escape at the very end of a string literal.","solutions":["Log the input around `start` to inspect the malformed escape before changing code","Pre-validate the input string: confirm it ends with a complete escape sequence before decoding","Treat the decode error as a decode failure for that item: fall back to the raw title or skip the item instead of failing the whole search","If the site systematically truncates payloads, re-fetch the page or use a different endpoint"],"exampleFix":"// before\nvalue, n, err := decodeHexEscape(data, i+2, 2)\nif err != nil {\n    return \"\", err\n}\n// after\nvalue, n, err := decodeHexEscape(data, i+2, 2)\nif err != nil {\n    log.Printf(\"haitunsou: bad escape at %d, using raw title\", i)\n    return string(data), nil // graceful fallback\n}","handlingStrategy":"try-catch","validationCode":"// Go: check remaining bytes before the call\nif start < 0 || start+length > len(data) {\n    return string(data), nil // use raw, skip escape decoding\n}","typeGuard":"func hasCompleteHexEscape(data []byte, start, length int) bool {\n    return start >= 0 && start+length <= len(data)\n}","tryCatchPattern":"value, n, err := decodeHexEscape(data, i+2, 2)\nif err != nil {\n    log.Printf(\"bad escape at %d: %v\", i, err)\n    return string(data), nil // fallback to raw string\n}","preventionTips":["Fuzz the decoder with truncated inputs","Always validate slice bounds before decoding escapes","Prefer per-item fallback over failing the whole search"],"tags":["go","parsing","hex-escape","web-scraping"],"backgroundTag":"invalid-argument-format","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}