Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HiveError: Cannot read, unknown typeId: 38. Did you forget to register an adapter? #332

Closed
stefanschaller opened this issue Jun 4, 2020 · 28 comments · Fixed by #397
Closed
Assignees
Labels
enhancement New feature or request

Comments

@stefanschaller
Copy link

Steps to Reproduce

Previous code before the issue

The registration of the adapter:

registerAdapter(ShelfAdapter());
registerAdapter(ShelfProductAdapter());

My Models + Adapter:

@HiveType(typeId: 4)
class Product implements Identifiable {
  @HiveField(0)
  @override
  final String id;
  @HiveField(1)
  final String name;

  @HiveField(2)
  final ProductType type;

  const Product({
    @required this.id,
    @required this.name,
    @required this.type,
  });
}

part of 'product.dart';

// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************

class ProductAdapter extends TypeAdapter<Product> {
  @override
  final typeId = 4;

  @override
  Product read(BinaryReader reader) {
    var numOfFields = reader.readByte();
    var fields = <int, dynamic>{
      for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
    };
    return Product(
      id: fields[0] as String,
      name: fields[1] as String,
      type: fields[2] as ProductType,
    );
  }

  @override
  void write(BinaryWriter writer, Product obj) {
    writer
      ..writeByte(3)
      ..writeByte(0)
      ..write(obj.id)
      ..writeByte(1)
      ..write(obj.name)
      ..writeByte(2)
      ..write(obj.type);
  }
}
part 'product_type.g.dart';

@HiveType(typeId: 4)
class ProductType {

  @HiveField(0)
  final int value;

  const ProductType(this.value);
}

part of 'product_type.dart';

// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************

class ProductTypeAdapter extends TypeAdapter<ProductType> {
  @override
  final typeId = 4;

  @override
  ProductType read(BinaryReader reader) {
    var numOfFields = reader.readByte();
    var fields = <int, dynamic>{
      for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
    };
    return ProductType(fields[0] as int);
  }

  @override
  void write(BinaryWriter writer, ProductType obj) {
    writer
      ..writeByte(1)
      ..writeByte(0)
      ..write(obj.value);
  }
}

So far, so good.
But I want to remove the child object / adapter because we don't need it from now on.
So let's completely remove the ProjectType and the ProjectTypeAdapter.

And not the App is crashing with the following Issue:

HiveError: Cannot read, unknown typeId: 38. Did you forget to register an adapter?

After a complete new installation everything is working fine.

Version

  • Platform: iOS, Android
  • Flutter version: 1.17.2 • channel stable
  • Package versions:
    • hive: ^1.4.1+1
    • hive_flutter: ^0.3.0+2
@stefanschaller stefanschaller added the problem An unconfirmed bug. label Jun 4, 2020
@MarcelGarus
Copy link
Contributor

I'm not completely sure about why the type id 38 gets mentioned… Hive internally uses 32 reserved type ids, so your id 4 should have become 32 + 4 = 36.

More importantly, AFAIK the current Hive architecture doesn't allow for removing adapters. It essentially saves "now come some bytes that the adapter with id 38 knows how to decode and encode". Then it asks the adapter to decode the values – if the adapter is missing, Hive doesn't know how to continue and aborts parsing.

@simc
Copy link
Member

simc commented Jun 4, 2020

AFAIK the current Hive architecture doesn't allow for removing adapters

Exactly. Hive uses the adapter to determine how many bytes each entry uses. When an adapter is missing, Hive cannot find the following entry.

Sorry, my precious answer was wrong: The entry does contain the length and we can just add an Hive.ignoreTypeId(38) method.

@simc simc added documentation Improvements or additions to documentation enhancement New feature or request help wanted Extra attention is needed and removed problem An unconfirmed bug. documentation Improvements or additions to documentation help wanted Extra attention is needed labels Jun 4, 2020
@D0xzen
Copy link

D0xzen commented Jun 21, 2020

I have the same problem, i think that there is something wrong cause when i used the typeId: 0 i get

HiveError: Cannot read, unknown typeId: 114.

for ids more than 0 i get the error on typeId 32

@joaovirgili
Copy link

Any solution of this? I have typedId from 0 to 33 and error says: Cannot read, unknown typeId: 69. Did you forget to register an adapter?. I don't know what to do

@D0xzen
Copy link

D0xzen commented Jul 29, 2020

Any solution of this? I have typedId from 0 to 33 and error says: Cannot read, unknown typeId: 69. Did you forget to register an adapter?. I don't know what to do

You have to add '@HiveType(typeId: 1)' at the top of you class name as you can see in this video https://www.youtube.com/watch?v=SFcMdQXnM78&t=619s at 10:19

I asked them to add it to the documentation before it wasnt there

@joaovirgili
Copy link

Any solution of this? I have typedId from 0 to 33 and error says: Cannot read, unknown typeId: 69. Did you forget to register an adapter?. I don't know what to do

You have to add '@HiveType(typeId: 1)' at the top of you class name as you can see in this video https://www.youtube.com/watch?v=SFcMdQXnM78&t=619s at 10:19

I asked them to add it to the documentation before it wasnt there

I've already done that. My code was working, but now is showing that error

@D0xzen
Copy link

D0xzen commented Jul 30, 2020

the only thing that i can think of is to try "flutter clean cache" or try to move from channel master to stable

@themisir
Copy link
Contributor

themisir commented Aug 2, 2020

AFAIK the current Hive architecture doesn't allow for removing adapters

Exactly. Hive uses the adapter to determine how many bytes each entry uses. When an adapter is missing, Hive cannot find the following entry.

Sorry, my precious answer was wrong: The entry does contain the length and we can just add an Hive.ignoreTypeId(38) method.

But we need to know how many bytes should we skip before reading next frame? Also some adapters might write variable length data. So using something like: Hive.ignoreTypeId(38, length: 8) - will not work too.

@simc
Copy link
Member

simc commented Aug 2, 2020

@themisir The first four bytes of any frame are the frame length so knowledge about the adapter is not needed to skip a frame.

@themisir
Copy link
Contributor

themisir commented Aug 2, 2020

Thanks, @leisim. I'll add HiveImpl.ignoreTypeId function asap.

@amebrahimi
Copy link

I have this problem HiveError: Cannot read, unknown typeId: 38. Did you forget to register an adapter? what should I do?

@themisir
Copy link
Contributor

themisir commented Aug 9, 2020

I have this problem HiveError: Cannot read, unknown typeId: 38. Did you forget to register an adapter? what should I do?

Have you registered the adapter using Hive.registerAdapter(..)? The adapters must be registered before Hive.openBox(..) calls

@amebrahimi
Copy link

Yes it was working before I've updated flutter to 1.20 after the update I've updated hive to 1.4.2 before that it was 1.4.1+1 and suddenly this error occurs.

@amebrahimi
Copy link

amebrahimi commented Aug 9, 2020

I've done flutter clean cache. Still getting the error.

@amebrahimi
Copy link

amebrahimi commented Aug 10, 2020

My error comes after second launch when I populate the box with data. At first launch I create the box and register the adapters as well and when I go to a specific page the box will get populated with data and I get no error. But in the second run when the app want's to read the data from the box this error shows up.

@D0xzen
Copy link

D0xzen commented Aug 10, 2020

My error comes after second launch when I populate the box with data. At first launch I create the box and register the adapters as well and when I go to a specific page the box will get populated with data and I get no error. But in the second run when the app want's to read the data from the box this error shows up.

What you can do after following the previous advices is try to delete the created db and create a new one, me when i change database name after flutter clean worked

@amebrahimi
Copy link

amebrahimi commented Aug 10, 2020

My init path is final appDocumentDirectory = await getApplicationSupportDirectory();. If I clear data the app the database shouldn't be deleted? Maybe the path_provider is causing the problem? 🤔

@amebrahimi
Copy link

I've updated path_provider from 1.6.10 to 1.6.11 and boom, problem has gone away.

@JulianBissekkou
Copy link

Looks like this issue is still happening. After adding a new case to my enum, hive throws this error when reading a class that has this enum property.

HiveError: Cannot read, unknown typeId: 62. Did you forget to register an adapter?

I tried calling ignoreTypeId<dynamic>(62); but this didn't help.
@leisim

@wbusey0
Copy link
Contributor

wbusey0 commented Mar 11, 2021

@themisir I ran into some troubles using ignoreTypeId and starting digging into the source code of your pull request #397

It seems that the BinaryWriterImpl uses findAdapterForValue to find the _ResolvedAdapter.

Which - if I'm not mistaken - means ignoreTypeId<T>(int typeId) results in

  1. ignored reads from the typeId passed
  2. ignored writes for any object matching following code:
    bool matches(dynamic value) => value is T;

This makes that when using the method without generics:
Hive.ignoreTypeId(0)
all writes will be ignored, as anything is dynamic will always be true and return the IgnoredTypeAdapter.
Does this mean you cannot delete the Hive-object from your class structure, because you need to pass it as a generics argument?

The name of the method is kind of misleading as it makes it seem just passing a typeId is fine. Furthermore, not passing a generics Type seems like a very easy mistake which results in very weird behaviour where:

  • any value is overwritten with null when being written, also the typeId is set to the typeId of the IgnoredTypeAdapter
  • reading the values of a box afterwards returns all null values, IgnoredTypeAdapter is used as well because the write set the typeId to the ignored one

We managed to work around this by calling:
Hive.ignoreTypeId<RandomClass>(0)
which

  • prevents us from registering typeId 0 again
  • ignores reads for typeId 0
  • ignores writes for RandomClass which is a dummy non-Hive class anyway

If you need more information, please let me know.

I'm not sure what the best fix for this would be. A mention of this problem in the documentation of ignoreTypeId would be very helpful in any case.

@themisir
Copy link
Contributor

@themisir I ran into some troubles using ignoreTypeId and starting digging into the source code of your pull request #397

It seems that the BinaryWriterImpl uses findAdapterForValue to find the _ResolvedAdapter.

Which - if I'm not mistaken - means ignoreTypeId<T>(int typeId) results in

  1. ignored reads from the typeId passed
  2. ignored writes for any object matching following code:
    bool matches(dynamic value) => value is T;

This makes that when using the method without generics:
Hive.ignoreTypeId(0)
all writes will be ignored, as anything is dynamic will always be true and return the IgnoredTypeAdapter.
Does this mean you cannot delete the Hive-object from your class structure, because you need to pass it as a generics argument?

The name of the method is kind of misleading as it makes it seem just passing a typeId is fine. Furthermore, not passing a generics Type seems like a very easy mistake which results in very weird behaviour where:

  • any value is overwritten with null when being written, also the typeId is set to the typeId of the IgnoredTypeAdapter
  • reading the values of a box afterwards returns all null values, IgnoredTypeAdapter is used as well because the write set the typeId to the ignored one

We managed to work around this by calling:
Hive.ignoreTypeId<RandomClass>(0)
which

  • prevents us from registering typeId 0 again
  • ignores reads for typeId 0
  • ignores writes for RandomClass which is a dummy non-Hive class anyway

If you need more information, please let me know.

I'm not sure what the best fix for this would be. A mention of this problem in the documentation of ignoreTypeId would be very helpful in any case.

Thanks for letting me know. I could write a type check to ignore (don't use for writes) adapters if T == dynamic so calling ignoreTypeId(0) will just ignore reading typeId == 0 fields / entries but will not be used for write operations.

@wbusey0
Copy link
Contributor

wbusey0 commented Mar 12, 2021

Hi TheMisir,

Thanks for the quick answer.
I think that's a good improvement!

I was just wondering, is it even necessary to have the generic parameter here? If you don't want to use a certain typeId anymore, the IgnoredTypeAdapter will do just fine without it (it'll prevent registering the same typeId).
As of now, it only impacts writing, but is it ever desired behaviour to just ignore all writes of a certain Type? It seems like a case you would want to get a HiveError for because you're still using a class that have been deprecated.

I understand that that would be a bigger/breaking change, and your suggested fix will work fine for us as well.
If you want an extra set of eyes or hands for discussion / review / testing / coding, happy to help!

@ari-borlaza
Copy link

ari-borlaza commented Oct 12, 2021

So far, so good. But I want to remove the child object / adapter because we don't need it from now on. So let's completely remove the ProjectType and the ProjectTypeAdapter.
And not the App is crashing with the following Issue:
HiveError: Cannot read, unknown typeId: 38. Did you forget to register an adapter?

What i did when I had this problem is I adjusted the typeIds of the other files after it.
(ex. I have typeId 0, 1, 2, 3 and I removed 2; I adjusted 3 to become 2)
Also remove the register adapter in main.dart

then I did
flutter packages pub run build_runner build --delete-conflicting-outputs
flutter clean
flutter clean cache
flutter pub get

Uninstalled and Installed the app

@ParthKabariy
Copy link

So far, so good. But I want to remove the child object / adapter because we don't need it from now on. So let's completely remove the ProjectType and the ProjectTypeAdapter.
And not the App is crashing with the following Issue:
HiveError: Cannot read, unknown typeId: 38. Did you forget to register an adapter?

What i did when I had this problem is I adjusted the typeIds of the other files after it. (ex. I have typeId 0, 1, 2, 3 and I removed 2; I adjusted 3 to become 2) Also remove the register adapter in main.dart

then I did flutter packages pub run build_runner build --delete-conflicting-outputs flutter clean flutter clean cache flutter pub get

Uninstalled and Installed the app

Thanks workaround but it will work if app is not on store. If app is on Store and user are already using it then we need to migrate hive table and it will crash if we change model typeId.

any one find out any solution for this?

@hm122
Copy link

hm122 commented Apr 9, 2022

Facing same issue.

@antonioreyna
Copy link

same here

@hydev777
Copy link

hydev777 commented May 10, 2023

After checking that all the classes were correct, I tried doing flutter clean, flutter clean cache, and wipe the data of the device emulator, and problem solved.

@minoesteban
Copy link

We just had the same problem. Removed an existing Adapter, and now every time we try to read old data (written when the removed adapter was around), we get that same HiveError.

We added the Hive.ignoreTypeId(N) line, and it keeps failing but now the error is RangeError: Not enough bytes available. So I guess it is failing to get the right field length of the ignored type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.