{"record":{"id":"e14b1e45b011a83f","repo":"beemdevelopment/Aegis","slug":"expected-a-class-desc-found","errorCode":null,"errorMessage":"Expected a class desc, found: ","messagePattern":"Expected a class desc, found: ","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java","lineNumber":388,"sourceCode":"        public static Map<String, String> parse(DataInputStream inStream)\n                throws IOException, ParseException {\n            Map<String, String> map = new HashMap<>();\n\n            // Read/validate the magic number and version\n            int magic = inStream.readUnsignedShort();\n            int version = inStream.readUnsignedShort();\n            if (magic != MAGIC || version != VERSION) {\n                throw new ParseException(\"Not a serialized Java Object\");\n            }\n\n            // Read the class descriptor info for HashMap\n            byte b = inStream.readByte();\n            if (b != TC_OBJECT) {\n                throw new ParseException(\"Expected an object, found: \" + b);\n            }\n            b = inStream.readByte();\n            if (b != TC_CLASSDESC) {\n                throw new ParseException(\"Expected a class desc, found: \" + b);\n            }\n            parseClassDescriptor(inStream);\n\n            // Not interested in the capacity of the map\n            inStream.readInt();\n            // Read the number of elements in the HashMap\n            int size = inStream.readInt();\n\n            // Parse each key-value pair in the map\n            for (int i = 0; i < size; i++) {\n                String key = parseStringObject(inStream);\n                String value = parseStringObject(inStream);\n                map.put(key, value);\n            }\n\n            return map;\n        }\n","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java#L370-L406","documentation":"FreeOtpImporter.parse expects the byte after TC_OBJECT to be TC_CLASSDESC (0x72), the class-descriptor marker for the serialized HashMap. When the byte differs, it throws this ParseException including the unexpected byte value, indicating the serialized stream does not follow the exact object/classdesc ordering the manual parser implements.","triggerScenarios":"A Java-serialized stream where the object tag is followed by something other than a class descriptor — e.g. TC_NULL, TC_REFERENCE, TC_PROXYCLASSDESC, or an object written with custom serialization that omits the classdesc — or byte corruption between the two tags.","commonSituations":"FreeOTP exports written by a different JVM serialization configuration, streams that serialize a non-HashMap top-level type, or files corrupted in transit (partial download, encoding conversion like base64 round-trips gone wrong).","solutions":["Compare the reported byte with the Java serialization tag table to identify what is actually encoded there.","Verify the top-level serialized type in the FreeOTP export is java.util.HashMap as the parser requires.","Re-export from FreeOTP and check the file is not truncated — corruption right after the header produces this.","Align FreeOTP and Aegis versions so the serialization layout matches what the importer supports."],"exampleFix":"// before: a stream whose top-level object is not HashMap\nObject root = someNonHashMapSerializedStream;\n// after: ensure the export is the expected HashMap token store\nbyte[] data = readAllBytes(f);\nif (!isHashMapSerializedStore(data)) { // checks TC_OBJECT then TC_CLASSDESC with java.util.HashMap\n    throw new ParseException(\"FreeOTP export does not contain the expected HashMap structure\");\n}","handlingStrategy":"validation","validationCode":"static void validateClassDescTag(byte[] data) {\n    // header(4) + TC_OBJECT(1) must be followed by TC_CLASSDESC (0x72)\n    if (data.length >= 6 && data[5] != 0x72) {\n        throw new ParseException(String.format(\"Expected TC_CLASSDESC, found 0x%02X at offset 5\", data[5]));\n    }\n}","typeGuard":"static boolean hasClassDescAfterObject(byte[] data) {\n    return data != null && data.length >= 6 && data[4] == 0x73 && data[5] == 0x72;\n}","tryCatchPattern":"try {\n    importer.parse(stream);\n} catch (ParseException e) {\n    if (e.getMessage().startsWith(\"Expected a class desc, found:\")) {\n        // top-level object is not backed by a plain class descriptor (e.g. TC_NULL/TC_REFERENCE)\n        throw new ImportException(\"Unsupported FreeOTP serialization layout\", e);\n    }\n}","preventionTips":["Validate the byte pattern AC ED 00 05 73 72 before parsing.","Confirm the top-level serialized type is java.util.HashMap, not a wrapper or null.","Check for truncation/corruption right after the header when files arrive over unreliable channels.","Regenerate the export rather than attempting to repair a corrupt stream."],"tags":["java","android","importer","serialization","freeotp"],"backgroundTag":"invalid-argument-format","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}