beemdevelopment/Aegis · error · ParseException
Expected a string object, found
Error message
Expected a string object, found: %d
What it means
SerializedHashMapParser.parseStringObject reads the next byte of the serialized FreeOTP stream expecting a TC_STRING marker (a Java serialized string object). Any other object type code means the parser's expectations about the HashMap's contents no longer line up with the actual stream, so it throws this ParseException.
Solutions
- Re-export tokens from the stock FreeOTP app and retry the import.
- Verify the file is a FreeOTP backup (serialized HashMap of String->String) rather than another serialized object.
- Check whether earlier skip logic (field descriptors, trailing 4 bytes) misaligned the stream for this producer version and adjust skipBytes accordingly.
- Dump the serialized stream to inspect object graph and confirm where the non-string object appears.
Example fix
// before
byte objectType = inputStream.readByte();
if (objectType != TC_STRING) {
throw new ParseException(String.format("Expected a string object, found: %d", objectType));
}
// after: tolerate known non-string markers where appropriate
byte objectType = inputStream.readByte();
if (objectType == TC_NULL) { return null; }
if (objectType != TC_STRING) {
throw new ParseException(String.format("Expected a string object, found: %d", objectType));
} Defensive patterns
Strategy: try-catch
Try / catch
try {
importer.importFromUri(uri);
} catch (DatabaseImporterException e) {
if (e.getMessage() != null && e.getMessage().contains("Expected a string object")) {
showError("The backup layout differs from what the importer expects; re-export from stock FreeOTP.");
}
} Prevention
- Use backups produced only by the stock FreeOTP app.
- Ensure the whole file is transferred intact (partial files desynchronize parsing).
- Test imports on a small export first.
- Keep Aegis updated so skip-logic matches current FreeOTP output.
When it happens
Trigger: During parse(), when consuming keys/values of the serialized HashMap, a byte that is not TC_STRING appears where a string object is expected — e.g. the map holds non-String values, the offset is misaligned due to earlier skipped bytes, or the file isn't a FreeOTP backup.
Common situations: Backups from modified FreeOTP versions storing extra object types; importing a different app's Java-serialized HashMap; a parsing offset bug after the class-descriptor skip produced desynchronized reads.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Unexpected serial version UID
- Unexpected field type
- 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/8956752099322243.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java:448
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);
return new String(strBytes, StandardCharsets.UTF_8);
}
private static String parseUTF(DataInputStream inputStream) throws IOException {
int length = inputStream.readUnsignedShort();
byte[] strBytes = new byte[length];
inputStream.readFully(strBytes);
return new String(strBytes, StandardCharsets.UTF_8);
}
private static class ParseException extends Exception {
public ParseException(String message) {View on GitHub (pinned to d6f4e5925a)