{"record":{"id":"1a14360f8b566579","repo":"skylot/jadx","slug":"string-read-error","errorCode":null,"errorMessage":"String read error","messagePattern":"String read error","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"jadx-core/src/main/java/jadx/core/clsp/ClsSet.java","lineNumber":472,"sourceCode":"\t\tif (len >= 0xFF) {\n\t\t\tthrow new JadxRuntimeException(\"String is too long: \" + name);\n\t\t}\n\t\twriteUnsignedByte(out, bytes.length);\n\t\tout.write(bytes);\n\t}\n\n\tprivate static String readString(DataInputStream in) throws IOException {\n\t\tint len = readUnsignedByte(in);\n\t\treturn readString(in, len);\n\t}\n\n\tprivate static String readString(DataInputStream in, int len) throws IOException {\n\t\tbyte[] bytes = new byte[len];\n\t\tint count = in.read(bytes);\n\t\twhile (count != len) {\n\t\t\tint res = in.read(bytes, count, len - count);\n\t\t\tif (res == -1) {\n\t\t\t\tthrow new IOException(\"String read error\");\n\t\t\t} else {\n\t\t\t\tcount += res;\n\t\t\t}\n\t\t}\n\t\treturn new String(bytes, STRING_CHARSET);\n\t}\n\n\tprivate static void writeUnsignedByte(DataOutputStream out, int value) throws IOException {\n\t\tif (value < 0 || value >= 0xFF) {\n\t\t\tthrow new JadxRuntimeException(\"Unsigned byte value is too big: \" + value);\n\t\t}\n\t\tout.writeByte(value);\n\t}\n\n\tprivate static int readUnsignedByte(DataInputStream in) throws IOException {\n\t\treturn ((int) in.readByte()) & 0xFF;\n\t}\n","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java#L454-L490","documentation":"Thrown as an IOException by ClsSet.readString(DataInputStream, int) when the input stream returns -1 (EOF) before the expected number of string bytes are read. The method loops reading partial bytes; hitting end-of-stream mid-string indicates a truncated or corrupted .clst file.","triggerScenarios":"Reading a .clst file that is shorter than expected — either truncated on disk, partially downloaded, or produced by an interrupted write. A version mismatch causing incorrect length decoding can also make jadx try to read past the actual data.","commonSituations":"Interrupted or incomplete .clst file generation. Disk corruption or partial file transfer. A version skew where readUnsignedByte decodes a length that doesn't match what was written, causing a read overrun.","solutions":["Delete the corrupted .clst file and regenerate or re-download it from a known-good source.","Verify file integrity (checksum/size) before loading.","Ensure the writing and reading jadx versions use the same STRING_CHARSET and length encoding."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate file integrity before loading\nFile clstFile = new File(path);\nlong expectedSize = ...; // known-good size or checksum\nif (clstFile.length() < expectedSize) {\n    throw new IOException(\"Possible truncated .clst file: \" + path);\n}\n// Verify checksum\nString checksum = DigestUtils.md5Hex(new FileInputStream(clstFile));\nif (!checksum.equals(EXPECTED_CHECKSUM)) {\n    throw new IOException(\"Checksum mismatch for .clst file\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    clsSet.loadFromClstFile();\n} catch (IOException e) {\n    if (e.getMessage().equals(\"String read error\")) {\n        logger.error(\"Corrupted or truncated .clst file. Re-download or regenerate.\", e);\n    }\n    throw e;\n}","preventionTips":["Verify .clst file checksums after download or generation.","Use atomic writes (temp file + rename) when generating .clst to avoid truncation.","Do not load .clst files from untrusted or incomplete sources."],"tags":["clsp","io","corrupted-file","classpath"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}