{"record":{"id":"6e2d29a6a31a233a","repo":"beemdevelopment/Aegis","slug":"expected-an-object-found","errorCode":null,"errorMessage":"Expected an object, found: ","messagePattern":"Expected an object, found: ","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java","lineNumber":384,"sourceCode":"        private SerializedHashMapParser() {\n\n        }\n\n        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            }","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java#L366-L402","documentation":"In FreeOtpImporter.parse, after validating the serialization magic and version, the parser expects the next byte to be TC_OBJECT (0x73), the marker that a Java-serialized object follows. Any other value means the stream deviates from the expected Java serialization layout, so a ParseException is thrown with the offending byte appended to the message.","triggerScenarios":"Parsing a stream whose 5th byte is not 0x73 — e.g. the file embeds a serialization stream with leading/trailing extra bytes, contains a different top-level tag (TC_NULL, TC_BLOCKDATA), or is corrupt after the header.","commonSituations":"FreeOTP exports that were concatenated with extra data, streams produced by a newer/older FreeOTP that serializes a different top-level structure, or files damaged during transfer (FTP ASCII mode, partial downloads).","solutions":["Check the reported byte value in the message against Java serialization tag constants to identify what the stream actually contains.","Re-export the FreeOTP data and verify the file is byte-identical (compare hashes) to a known-good export.","Strip any wrapper/extra bytes so the stream begins exactly at the AC ED header.","Update FreeOTP/Aegis in case the format version diverged."],"exampleFix":"// before: parsing a stream with junk bytes before the object tag\nparse(new DataInputStream(new FileInputStream(f))); // offset by 1 -> b != TC_OBJECT\n// after: locate the real stream start first\nbyte[] data = readAllBytes(f);\nint start = indexOfJavaMagic(data); // index of 0xAC 0xED\nparse(new DataInputStream(new ByteArrayInputStream(data, start, data.length - start)));","handlingStrategy":"validation","validationCode":"static void validateStreamTags(byte[] data) {\n    // after the 4-byte header the stream must carry TC_OBJECT (0x73)\n    if (data.length >= 5 && data[4] != 0x73) {\n        throw new ParseException(String.format(\"Expected TC_OBJECT, found 0x%02X at offset 4\", data[4]));\n    }\n}","typeGuard":"static boolean startsWithObjectTag(byte[] data) {\n    return data != null && data.length >= 5 && data[4] == 0x73;\n}","tryCatchPattern":"try {\n    importer.parse(stream);\n} catch (ParseException e) {\n    if (e.getMessage().startsWith(\"Expected an object, found:\")) {\n        logStreamBytes(stream); // dump the first bytes to find embedded wrappers\n        throw new ImportException(\"FreeOTP stream has unexpected top-level tag\", e);\n    }\n}","preventionTips":["Inspect the first ~8 bytes of the export (AC ED 00 05 73 72 ...) before importing.","Avoid any transfer mode or tool that can prepend/append bytes (e.g. FTP ASCII).","Verify export integrity with a checksum against the original device.","Fail fast with the offending byte offset when hand-parsing serialization streams."],"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"}