Open Bringoff opened 3 years ago
Hive adds 32 to your typeId (the first 32 are reserved for Hive types).
So it might actually be typeId
32 that causes the problem?
No, typeId was 5 here.
Did you specify the generics parameters when registering the type adapters? Could you share the code where you are registering the adapters?
The problem is it was 4 months ago and we already went away with SQLite. But as I remember we fixed that problem by declaring separate type CollectionOfStores
with separate typeId
and a single field that held List<StoreEntity>
.
I don't use object, I just use normal with key and value . and now i get that error too.
Basically this issue happens when:
So when hive reads database, it detects that something was there with typeid of X and tries to deserialize (convert binary into runtime object) using registered adapter that's typeid is X. But hive couldn't found the adapter because it's removed / typeid is changed. So hive returns that error because it couldn't deserialize the data.
This part was written in Updating a class section of hive documentation.
To fix the issue you have to do one of the given solutions:
Hive.ignoreTypeId<MyType>(X)
@TheMisir basically, that’s not the case 🙂 I’ve described a situation that appears without touching typeId. Register an adapter for an object, and then put a list of these objects to a box with a single key.
Oh, thanks for letting me know. I'll investigate it ASAP.
I have the same issue I have 3 HiveType 1, 2 and 3 with their adapters type 2 contain one type 1 when I save one type 2 and restart the app throw me Unhandled Exception: HiveError: Cannot read, unknown typeId: 132. Did you forget to register an adapter? just saving 1 item not a list
I think I have the same issue, I have several hive objects but it happens only in one that has the type List<String>
. and not on the first or second run of the program (using dart native not flutter).
Hi,
I Have the same issue too. In my case, I have a lot of types but it happens when I added a type which encapsulate a List<int>
field. Before that I had a lots of type which have fields of this type or List
E/flutter (25895): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: HiveError: Cannot read, unknown typeId: 73. Did you forget to register an adapter?
My typeId for this new type is 19. Hope this can help.
Thanks in advance.
Having the same issue here
Same here, attached the firebase log:
I'm never used adapter in my code, the hive works fine for long time, but one day (today!) just stopped and I saw the crash in firebase.
here's my code snippet:
AndroidOptions _getAndroidOptions() => const AndroidOptions(encryptedSharedPreferences: true);
FlutterSecureStorage secureStorage = FlutterSecureStorage(aOptions: _getAndroidOptions());
String? encryptionKeyString = await secureStorage.read(key: 'key');
if (encryptionKeyString == null) {
final key = Hive.generateSecureKey();
await secureStorage.write(
key: 'key',
value: base64UrlEncode(key),
);
}
String? key = await secureStorage.read(key: 'key');
final encryptionKeyUint8List = base64Url.decode(key!);
Directory dir = await getApplicationSupportDirectory();
await Hive.initFlutter(dir.path);
pref = await Hive.openBox<Object>(PREF_BOX, encryptionCipher: HiveAesCipher(encryptionKeyUint8List)); <--- (prefs.dart:60)
Hi @simc, @themisir,
We're having exactly the same problem as @xOldeVx: we only have one box, encrypted, all keys with simple string values, no adapters. Many of our users get the error message "HiveError: Cannot read, unknown typeId: 120. Did you forget to register an adapter?" with different values for typeId. They say they can use the app for a while, then after a few uses they get the error. The issues have been raised on iOS as well as Android.
We use : hive: ^2.2.3 hive_flutter: ^1.1.0 flutter: ^3.7.12
Here's an extract from our code:
class CacheStorage implements Storage {
dynamic _secureKey;
final String _secureKeyKey = 'encrypted-key';
late Box _box;
late FlutterSecureStorage _secureStorage;
AndroidOptions get _androidOptions => const AndroidOptions(encryptedSharedPreferences: true);
IOSOptions get _iosOptions => const IOSOptions(accessibility: KeychainAccessibility.unlocked_this_device);
Future<void> init() async {
try {
await Hive.initFlutter();
_secureStorage = FlutterSecureStorage(aOptions: _androidOptions);
await _ensureSecureKey();
_box = await Hive.openBox('cache', encryptionCipher: _secureKey != null ? HiveAesCipher(_secureKey) : null);
} catch (e, stack) {
Sentry.captureException(e, stackTrace: stack);
}
}
/// Retrieve or create an encryption key to secure the Hive box and its data.
/// The key must be stored outside of the Hive's box but still securely, therefore flutter_secure_storage is used to cache it.
Future<void> _ensureSecureKey() async {
final existingKey = await _secureStorage.read(key: _secureKeyKey, aOptions: _androidOptions, iOptions: _iosOptions);
if (existingKey != null) {
_secureKey = base64Url.decode(existingKey);
} else {
_secureKey = Hive.generateSecureKey();
await _secureStorage.write(key: _secureKeyKey, value: base64UrlEncode(_secureKey), aOptions: _androidOptions, iOptions: _iosOptions);
}
}
...
}
And here is the stack trace:
HiveError: HiveError: Cannot read, unknown typeId: 120. Did you forget to register an adapter?
File "binary_reader_impl.dart", line 325, in BinaryReaderImpl.read
File "binary_reader_impl.dart", line 342, in BinaryReaderImpl.readEncrypted
File "binary_reader_impl.dart", line 278, in BinaryReaderImpl.readFrame
File "frame_helper.dart", line 21, in FrameHelper.framesFromBytes
File "frame_io_helper.dart", line 42, in FrameIoHelper.framesFromFile
File "<asynchronous suspension>"
File "storage_backend_vm.dart", line 86, in StorageBackendVm.initialize
File "<asynchronous suspension>"
File "hive_impl.dart", line 111, in HiveImpl._openBox
File "<asynchronous suspension>"
File "hive_impl.dart", line 142, in HiveImpl.openBox
File "<asynchronous suspension>"
File "cache_storage.dart", line 29, in CacheStorage.init
File "<asynchronous suspension>"
File "app.dart", line 48, in appSetup
File "<asynchronous suspension>"
File "main_prod.dart", line 28, in main.<fn>
File "<asynchronous suspension>"
File "<asynchronous suspension>"
Using Hive.deleteBoxFromDisk is not an option for us or users will have to reconnect their account again and again.
Please help us, thank you very much in advance for your time and your help.
Did anyone solve this issue?
In our case Cannot read, unknown typeId
Issue was caused by keys that were longer than the allowed 256 chars (cause our code to get the keys was stupid). Cause length is only checked in assert, longer keys will weird behaviour in release build.
I am getting this error as well, unexpectedly since 2 days now and fails my android app's startup sequence.
"HiveError: Cannot read, unknown typeId: 64. Did you forget to register an adapter?"
I have registered all my custom adapters. In my case, I have a Map<MyEnum, int> in my custom class that is throwing this error when it tried to read in the adapter code. MyEnum adapter is also registered and no change was made to all this code. It was working fine for months.
The problem began some time after I added a new field (a List
Any help would be appreciated. The alternative is to write a bunch of serialization deser code to workaround all these fields that were implemented with Maps and Lists, causing a lot of maintenance overhead.
This thread has been open since 2020 -- any hints/ideas/resolutions so far?
--- Update: I tried converting the probably offending fields into standard types by flattening and string-ifying with custom code. Still getting the same error unfortunately. And now I have run out of ideas because its not clear what is causing this. Debugger doesnt seem to help trace anything much either.
@themisir , are you still involved in this project to help take a look?
Ok, I may have figured out how to fix it for my case but not clear what the root cause is in the hive library. I have tested for android app on multiple devices now.
For this particular custom class I had to:
writeInt
or readBool
functions, but instead ONLY use the read() write() methods for all fields. I found no comments saying mixed types were not allowed.Unpredictable behavior 😞 cost me days.
I dont know why, but this is working. I also dont know how it was working earlier when i used mixed types in this class. I also dont know when the other classes in this app will break since mixed types continue to work fine there.
I hope the above fix helps anyone else facing similar issues too. If anyone finds a real solution please post back.
Rewrote my class TypeAdapter to not use any of the writeInt or readBool functions, but instead ONLY use the read() write() methods for all fields. I found no comments saying mixed types were not allowed.
My guess is this one fixed the problem. The problem usually occurs when the serializer is implemented wrongly or you change serializer implementation without considering backwards compatibility (you try to read a value that's not previously written). This is the case for both custom adapters and adapters generated using hive-generator.
Unfortunately this is one of the hard problems in terms of binary serialization, you just have to be careful. If you don't want to deal with it in any way, you can prefer to use databases with high level schemas: sqlite3, isar, and more I can't currently remember the name.
Thanks @themisir ... As I pointed out earlier, the code has been working as expected for months. There was no change made to the existing fields or adapters. I added 1 new Enum field at the end and that broke the re-open Hive box path during app startup sequence. And reverting that enum also did not help anymore. So I doubt its because serializer was implemented the wrong way or was incompatible with anything there. I currently still have the flexibility to kill the DB and try out etc.. but not for long. So this makes me hesitate about using the lib in production. Based on the thread above there are cases where this happens and is usually with data structures like lists/maps. Its not about registering custom adapters.
This library so far has been a great experience for me to build my app and iterate fast.. It would be helpful if the library could offer better error messages, and info on what type is actually breaking. It took me over a day to realize the problem was not in my new field but was coming from an older unchanged field :) This kills dev productivity.
I understand your concerns. It is just not really possible to provide any further details on the error message. At least the current standard for writing ".hive" files doesn't contain any debugging information or field size [^1] details in order for us to consistently read fields or provide helpful error messages. That has been done to reduce disk space (no need to store additional metadata) use and improve performance (we can just read fields sequentially without having to worry about additional metadata processing).
This is not to say it is totally users' fault when the above error happens. I remember there were an internal bug that was causing hive to misbehave in certain scenarios, so it could be possible that there's a similar bug, but without ability to properly reproduce the bug it is unlikely to be found.
Honestly I would suggest using a proper RDBMS like sqlite3 if you are intending to store complex data structures, and use hive for simple key value storage (ie: to store user preferences or other miscellaneous stuff). While that would increase development complexity, it would pay off on a long run.
[^1]: Hive has field size metadata written for dynamically sized fields (strings, lists, raw bytes, custom adapters). However, this is not enough to consistently read fields. For example if you change type of an int field into bool, hive will read 1 byte instead of 4. And then it will assume the next 3 bytes are part of the following field.
There are multiple issues with similar problems, but no actual solution or explanation.
Steps to Reproduce
And I get an error
HiveError: Cannot read, unknown typeId: 64. Did you forget to register an adapter?
. But I don't have that much types registered. Adapters are registered inmain
function beforeHive.initFlutter()
(if I do init before registering adapters, it doesn't change anything).Code sample
Version