beemdevelopment/Aegis · error · ParseException
Expected an object, found:
Error message
Expected an object, found:
What it means
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.
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.
Example fix
// before: parsing a stream with junk bytes before the object tag parse(new DataInputStream(new FileInputStream(f))); // offset by 1 -> b != TC_OBJECT // after: locate the real stream start first byte[] data = readAllBytes(f); int start = indexOfJavaMagic(data); // index of 0xAC 0xED parse(new DataInputStream(new ByteArrayInputStream(data, start, data.length - start)));
Defensive patterns
Strategy: validation
Validate before calling
static void validateStreamTags(byte[] data) {
// after the 4-byte header the stream must carry TC_OBJECT (0x73)
if (data.length >= 5 && data[4] != 0x73) {
throw new ParseException(String.format("Expected TC_OBJECT, found 0x%02X at offset 4", data[4]));
}
} Type guard
static boolean startsWithObjectTag(byte[] data) {
return data != null && data.length >= 5 && data[4] == 0x73;
} Try / catch
try {
importer.parse(stream);
} catch (ParseException e) {
if (e.getMessage().startsWith("Expected an object, found:")) {
logStreamBytes(stream); // dump the first bytes to find embedded wrappers
throw new ImportException("FreeOTP stream has unexpected top-level tag", e);
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Not a serialized Java Object
- Expected a class desc, found:
- Unexpected class name
- Unable to find nonce in parameters
- Unable to decode stream to bitmap
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/6e2d29a6a31a233a.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java:384
private SerializedHashMapParser() {
}
public static Map<String, String> parse(DataInputStream inStream)
throws IOException, ParseException {
Map<String, String> map = new HashMap<>();
// Read/validate the magic number and version
int magic = inStream.readUnsignedShort();
int version = inStream.readUnsignedShort();
if (magic != MAGIC || version != VERSION) {
throw new ParseException("Not a serialized Java Object");
}
// Read the class descriptor info for HashMap
byte b = inStream.readByte();
if (b != TC_OBJECT) {
throw new ParseException("Expected an object, found: " + b);
}
b = inStream.readByte();
if (b != TC_CLASSDESC) {
throw new ParseException("Expected a class desc, found: " + b);
}
parseClassDescriptor(inStream);
// Not interested in the capacity of the map
inStream.readInt();
// Read the number of elements in the HashMap
int size = inStream.readInt();
// Parse each key-value pair in the map
for (int i = 0; i < size; i++) {
String key = parseStringObject(inStream);
String value = parseStringObject(inStream);
map.put(key, value);
}View on GitHub (pinned to d6f4e5925a)