{"record":{"id":"98568fe77d085202","repo":"beemdevelopment/Aegis","slug":"unexpected-class-name-s","errorCode":null,"errorMessage":"Unexpected class name: %s","messagePattern":"Unexpected class name: (.+?)","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java","lineNumber":412,"sourceCode":"            // 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\n        private static void parseClassDescriptor(DataInputStream inputStream)\n                throws IOException, ParseException {\n            // Check whether we're dealing with a HashMap and a version we support\n            String className = parseUTF(inputStream);\n            if (!className.equals(HashMap.class.getName())) {\n                throw new ParseException(String.format(\"Unexpected class name: %s\", className));\n            }\n            long serialVersionUID = inputStream.readLong();\n            if (serialVersionUID != SERIAL_VERSION_UID) {\n                throw new ParseException(String.format(\"Unexpected serial version UID: %d\", serialVersionUID));\n            }\n\n            // Read past all of the fields in the class\n            byte fieldDescriptor = inputStream.readByte();\n            if (fieldDescriptor == TC_NULL) {\n                return;\n            }\n            int totalFieldSkip = 0;\n            int fieldCount = inputStream.readUnsignedShort();\n            for (int i = 0; i < fieldCount; i++) {\n                char fieldType = (char) inputStream.readByte();\n                parseUTF(inputStream);\n                switch (fieldType) {\n                    case 'F': // float (4 bytes)","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java#L394-L430","documentation":"parseClassDescriptor reads the UTF class name from the Java-serialized class descriptor and requires it to be exactly java.util.HashMap; any other class name triggers this ParseException with the found name. The FreeOTP importer only supports the specific HashMap-at-top-level serialization layout, so unexpected classes mean the file is not the token store format it can decode.","triggerScenarios":"A Java serialization stream whose class descriptor names any class other than java.util.HashMap — e.g. java.util.Hashtable, a custom FreeOTP token class, or a serialized object from a different app placed in the same file.","commonSituations":"Importing an export from a different authenticator app, a FreeOTP version that changed its storage layout, or mixing up files (settings store vs token store) both of which are Java-serialized.","solutions":["Check the class name printed in the message — it tells you what the file actually contains.","Re-export the token store from FreeOTP, making sure to pick the token backup, not another serialized file.","Ensure the FreeOTP version matches the format Aegis's importer expects (update Aegis if FreeOTP is newer).","Verify the class descriptor's serialVersionUID also matches after fixing the class name — the parser checks it next."],"exampleFix":"// before: importing a wrong serialized file\nFile f = new File(\"freeotp_prefs.bin\"); // serializes SharedPreferences, not HashMap\n// after: select the actual token store\nFile f = new File(\"freeotp_tokens.bin\"); // serializes java.util.HashMap\ndef parseTokenStore(File f) { parse(f); }","handlingStrategy":"validation","validationCode":"static String peekSerializedClassName(byte[] data) {\n    // header(4) + TC_OBJECT + TC_CLASSDESC + 2-byte length -> then UTF class name\n    if (data == null || data.length < 8) return null;\n    int nameLen = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);\n    if (data.length < 8 + nameLen) return null;\n    return new String(data, 8, nameLen, StandardCharsets.UTF_8);\n}\n// usage: require \"java.util.HashMap\" before importing","typeGuard":"static boolean isHashMapClassDescriptor(byte[] data) {\n    String name = peekSerializedClassName(data);\n    return \"java.util.HashMap\".equals(name);\n}","tryCatchPattern":"try {\n    importer.parse(stream);\n} catch (ParseException e) {\n    if (e.getMessage().startsWith(\"Unexpected class name:\")) {\n        String actual = e.getMessage().substring(e.getMessage().indexOf(':') + 2);\n        throw new ImportException(\"File contains \" + actual + \" instead of a FreeOTP HashMap token store\", e);\n    }\n}","preventionTips":["Decode the class name from the class descriptor before running the importer and require java.util.HashMap.","Verify you are importing FreeOTP's token backup, not another Java-serialized file (prefs, settings).","Also pre-check serialVersionUID 2768647097183612297 (HashMap's) to catch version drift early.","Keep exporter and importer app versions in sync with the supported format."],"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"}