beemdevelopment/Aegis · error · ParseException
Unexpected field type
Error message
Unexpected field type: %s
What it means
While skipping the field descriptors of the serialized HashMap class, parseClassDescriptor recognizes only primitive field type codes it knows ('F' float, 'I' int, plus other handled cases above the shown snippet). An unknown Java serialization field type code means the class descriptor layout differs from what FreeOTP is expected to write, so parsing aborts.
Solutions
- Re-export the tokens using the stock FreeOTP app so the class descriptor matches the expected layout.
- Confirm the file is a genuine FreeOTP backup and not another app's serialized data.
- Extend the switch in parseClassDescriptor to handle the additional field type codes (e.g. 'J' long, 'D' double, 'Z' boolean) with correct byte widths.
- Inspect the stream with a Java serialization dumper to identify the unexpected field type.
Example fix
// before
default:
throw new ParseException(String.format("Unexpected field type: %s", fieldType));
// after
case 'J': // long (8 bytes)
totalFieldSkip += 8;
break;
case 'D': // double (8 bytes)
totalFieldSkip += 8;
break;
default:
throw new ParseException(String.format("Unexpected field type: %s", fieldType)); Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the source is stock FreeOTP before import
boolean isKnownSource = backupMetadata.getString("producer", "").equals("FreeOTP");
if (!isKnownSource) throw new IllegalArgumentException("Unsupported backup producer"); Try / catch
try {
importer.importFromUri(uri);
} catch (DatabaseImporterException e) {
if (e.getMessage() != null && e.getMessage().contains("Unexpected field type")) {
showError("Backup contains fields this importer doesn't support; update Aegis or re-export from stock FreeOTP.");
}
} Prevention
- Re-export from the stock FreeOTP app rather than forks or modified builds.
- Update Aegis before importing so newer field layouts are handled.
- Inspect unknown files with a Java serialization dumper first.
- Avoid hand-editing serialized backups.
When it happens
Trigger: A FreeOTP backup whose serialized HashMap class declares a field with a type code the skip-logic doesn't handle (e.g. arrays, objects, doubles, longs not covered by the switch), indicating a different producing app/version.
Common situations: Importing backups from FreeOTP forks that added fields to the stored map; importing a non-FreeOTP Java-serialized file; a corrupted stream where the field descriptor byte is garbage.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Unexpected serial version UID
- Expected a string object, found
- Unable to find nonce in parameters
- Not a serialized Java Object
- Expected an object, found:
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/f343dfc1735fa7bb.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java:435
}
// Read past all of the fields in the class
byte fieldDescriptor = inputStream.readByte();
if (fieldDescriptor == TC_NULL) {
return;
}
int totalFieldSkip = 0;
int fieldCount = inputStream.readUnsignedShort();
for (int i = 0; i < fieldCount; i++) {
char fieldType = (char) inputStream.readByte();
parseUTF(inputStream);
switch (fieldType) {
case 'F': // float (4 bytes)
case 'I': // int (4 bytes)
totalFieldSkip += 4;
break;
default:
throw new ParseException(String.format("Unexpected field type: %s", fieldType));
}
}
inputStream.skipBytes(totalFieldSkip);
// Not sure what these bytes are, just skip them
inputStream.skipBytes(4);
}
private static String parseStringObject(DataInputStream inputStream)
throws IOException, ParseException {
byte objectType = inputStream.readByte();
if (objectType != TC_STRING) {
throw new ParseException(String.format("Expected a string object, found: %d", objectType));
}
int length = inputStream.readUnsignedShort();
byte[] strBytes = new byte[length];
inputStream.readFully(strBytes);View on GitHub (pinned to d6f4e5925a)