Skip to content
New issue

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

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

Already on GitHub? # to your account

Web Unexpected null value #967

Closed
KiraKmp opened this issue May 17, 2022 · 8 comments · Fixed by #968
Closed

Web Unexpected null value #967

KiraKmp opened this issue May 17, 2022 · 8 comments · Fixed by #968
Labels
problem An unconfirmed bug.

Comments

@KiraKmp
Copy link

KiraKmp commented May 17, 2022

_hiveBox = await Hive.openBox(_boxName)

In web when trying to open box, getting Unexpected null value exception.

Occurring on the latest version 2.2.0, could not find this issue in 2.1.0

@KiraKmp KiraKmp added the problem An unconfirmed bug. label May 17, 2022
@Ralegond
Copy link

same bug

@Ahmadre
Copy link

Ahmadre commented May 17, 2022

@leisim

Bildschirmfoto 2022-05-17 um 18 14 57

I found the error! It's the new enhancement from v. 2.2.0

@TheOneWithTheBraid
Copy link

TheOneWithTheBraid commented May 17, 2022

I will fix this until tomorrow. It seems like it's not a good idea to merge things with a failing pipeline.

@nosmirck
Copy link

nosmirck commented May 17, 2022

I'm having the exact same issue. Here's the counter sample app (after I made changes to make it compatible with latest flutter version)

import 'dart:async';
import 'dart:collection';
import 'dart:math';
import 'dart:typed_data';

import 'package:hive/hive.dart';
import 'package:hive/src/adapters/big_int_adapter.dart';
import 'package:hive/src/adapters/date_time_adapter.dart';
import 'package:hive/src/backend/storage_backend_memory.dart';
import 'package:hive/src/box/box_base_impl.dart';
import 'package:hive/src/box/box_impl.dart';
import 'package:hive/src/box/default_compaction_strategy.dart';
import 'package:hive/src/box/default_key_comparator.dart';
import 'package:hive/src/box/lazy_box_impl.dart';
import 'package:hive/src/registry/type_registry_impl.dart';
import 'package:hive/src/util/extensions.dart';
import 'package:meta/meta.dart';

import 'backend/storage_backend.dart';

/// Not part of public API
class HiveImpl extends TypeRegistryImpl implements HiveInterface {
  final _boxes = HashMap<String, BoxBaseImpl>();
  final _openingBoxes = HashMap<String, Future>();
  BackendManagerInterface? _manager;
  final Random _secureRandom = Random.secure();

  /// Not part of public API
  @visibleForTesting
  String? homePath;

  /// Not part of public API
  HiveImpl();

  /// Not part of public API
  @visibleForTesting
  HiveImpl.debug(this._manager) {
    _registerDefaultAdapters();
  }

  /// Not part of public API
  @visibleForTesting
  HiveImpl.test() : _manager = BackendManager.select() {
    registerAdapter(DateTimeAdapter<DateTime>(), internal: true);
    registerAdapter(BigIntAdapter(), internal: true);
  }

  void _registerDefaultAdapters() {
    registerAdapter(DateTimeWithTimezoneAdapter(), internal: true);
    registerAdapter(DateTimeAdapter<DateTimeWithoutTZ>(), internal: true);
    registerAdapter(BigIntAdapter(), internal: true);
  }

  @override
  void init(
    String? path, {
    HiveStorageBackendPreference backendPreference =
        HiveStorageBackendPreference.native,
  }) {
    homePath = path;
    _manager ??= BackendManager.select(backendPreference);
    _registerDefaultAdapters();
  }

  Future<BoxBase<E>> _openBox<E>(
    String name,
    bool lazy,
    HiveCipher? cipher,
    KeyComparator comparator,
    CompactionStrategy compaction,
    bool recovery,
    String? path,
    Uint8List? bytes,
    String? collection,
  ) async {
    assert(path == null || bytes == null);
    assert(name.length <= 255 && name.isAscii,
        'Box names need to be ASCII Strings with a max length of 255.');
    name = name.toLowerCase();
    if (isBoxOpen(name)) {
      if (lazy) {
        return lazyBox(name);
      } else {
        return box(name);
      }
    } else {
      if (_openingBoxes.containsKey(name)) {
        await _openingBoxes[name];
        if (lazy) {
          return lazyBox(name);
        } else {
          return box(name);
        }
      }

      var completer = Completer();
      _openingBoxes[name] = completer.future;

      BoxBaseImpl<E>? newBox;
      try {
        StorageBackend backend;
        if (bytes != null) {
          backend = StorageBackendMemory(bytes, cipher);
        } else {
          backend = await _manager!
              .open(name, path ?? homePath, recovery, cipher, collection);
        }

        if (lazy) {
          newBox = LazyBoxImpl<E>(this, name, comparator, compaction, backend);
        } else {
          newBox = BoxImpl<E>(this, name, comparator, compaction, backend);
        }

        await newBox.initialize();
        _boxes[name] = newBox;

        completer.complete();
        return newBox;
      } catch (error, stackTrace) {
        newBox?.close();
        completer.completeError(error, stackTrace);
        rethrow;
      } finally {
        // ignore: unawaited_futures
        _openingBoxes.remove(name);
      }
    }
  }

  @override
  Future<Box<E>> openBox<E>(
    String name, {
    HiveCipher? encryptionCipher,
    KeyComparator keyComparator = defaultKeyComparator,
    CompactionStrategy compactionStrategy = defaultCompactionStrategy,
    bool crashRecovery = true,
    String? path,
    Uint8List? bytes,
    String? collection,
    @Deprecated('Use encryptionCipher instead') List<int>? encryptionKey,
  }) async {
    if (encryptionKey != null) {
      encryptionCipher = HiveAesCipher(encryptionKey);
    }
    return await _openBox<E>(name, false, encryptionCipher, keyComparator,
        compactionStrategy, crashRecovery, path, bytes, collection) as Box<E>;
  }

  @override
  Future<LazyBox<E>> openLazyBox<E>(
    String name, {
    HiveCipher? encryptionCipher,
    KeyComparator keyComparator = defaultKeyComparator,
    CompactionStrategy compactionStrategy = defaultCompactionStrategy,
    bool crashRecovery = true,
    String? path,
    String? collection,
    @Deprecated('Use encryptionCipher instead') List<int>? encryptionKey,
  }) async {
    if (encryptionKey != null) {
      encryptionCipher = HiveAesCipher(encryptionKey);
    }
    return await _openBox<E>(
        name,
        true,
        encryptionCipher,
        keyComparator,
        compactionStrategy,
        crashRecovery,
        path,
        null,
        collection) as LazyBox<E>;
  }

  BoxBase<E> _getBoxInternal<E>(String name, [bool? lazy]) {
    var lowerCaseName = name.toLowerCase();
    var box = _boxes[lowerCaseName];
    if (box != null) {
      if ((lazy == null || box.lazy == lazy) && box.valueType == E) {
        return box as BoxBase<E>;
      } else {
        var typeName = box is LazyBox
            ? 'LazyBox<${box.valueType}>'
            : 'Box<${box.valueType}>';
        throw HiveError('The box "$lowerCaseName" is already open '
            'and of type $typeName.');
      }
    } else {
      throw HiveError('Box not found. Did you forget to call Hive.openBox()?');
    }
  }

  /// Not part of public API
  BoxBase? getBoxWithoutCheckInternal(String name) {
    var lowerCaseName = name.toLowerCase();
    return _boxes[lowerCaseName];
  }

  @override
  Box<E> box<E>(String name) => _getBoxInternal<E>(name, false) as Box<E>;

  @override
  LazyBox<E> lazyBox<E>(String name) =>
      _getBoxInternal<E>(name, true) as LazyBox<E>;

  @override
  bool isBoxOpen(String name) {
    return _boxes.containsKey(name.toLowerCase());
  }

  @override
  Future<void> close() {
    var closeFutures = _boxes.values.map((box) {
      return box.close();
    });

    return Future.wait(closeFutures);
  }

  /// Not part of public API
  void unregisterBox(String name) {
    name = name.toLowerCase();
    _openingBoxes.remove(name);
    _boxes.remove(name);
  }

  @override
  Future<void> deleteBoxFromDisk(String name,
      {String? path, String? collection}) async {
    var lowerCaseName = name.toLowerCase();
    var box = _boxes[lowerCaseName];
    if (box != null) {
      await box.deleteFromDisk();
    } else {
      await _manager!.deleteBox(lowerCaseName, path ?? homePath, collection);
    }
  }

  @override
  Future<void> deleteFromDisk() {
    var deleteFutures = _boxes.values.toList().map((box) {
      return box.deleteFromDisk();
    });

    return Future.wait(deleteFutures);
  }

  @override
  List<int> generateSecureKey() {
    return _secureRandom.nextBytes(32);
  }

  @override
  Future<bool> boxExists(String name,
      {String? path, String? collection}) async {
    var lowerCaseName = name.toLowerCase();
    return await _manager!
        .boxExists(lowerCaseName, path ?? homePath, collection);
  }
}

The app running on Chrome, macOS outputs this stack trace:

Launching lib/main.dart on Chrome in debug mode...
This app is linked to the debug service: ws://127.0.0.1:50205/bafXWtTTiDA=/ws
Debug service listening on ws://127.0.0.1:50205/bafXWtTTiDA=/ws
💪 Running with sound null safety 💪
Connecting to VM Service at ws://127.0.0.1:50205/bafXWtTTiDA=/ws
Flutter Web Bootstrap: Programmatic
Unexpected null value.
Error: Unexpected null value.
    at Object.throw_ [as throw] (http://localhost:50156/dart_sdk.js:5080:11)
    at Object.nullCheck (http://localhost:50156/dart_sdk.js:5399:30)
at hive_impl.HiveImpl.new._openBox (http://localhost:50156/packages/hive/src/box_collection.dart.lib.js:3816:37)
    at _openBox.next (<anonymous>)
    at runBody (http://localhost:50156/dart_sdk.js:40660:34)
    at Object._async [as async] (http://localhost:50156/dart_sdk.js:40691:7)
at hive_impl.HiveImpl.new.[_openBox] (http://localhost:50156/packages/hive/src/box_collection.dart.lib.js:3788:20)
at hive_impl.HiveImpl.new.openBox (http://localhost:50156/packages/hive/src/box_collection.dart.lib.js:3856:52)
    at openBox.next (<anonymous>)
    at runBody (http://localhost:50156/dart_sdk.js:40660:34)
    at Object._async [as async] (http://localhost:50156/dart_sdk.js:40691:7)
at hive_impl.HiveImpl.new.openBox (http://localhost:50156/packages/hive/src/box_collection.dart.lib.js:3852:20)
at main.MyApp.new.build (http://localhost:50156/packages/hydrated_bloc_test/main.dart.lib.js:643:216)
at framework.StatelessElement.new.build (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:53902:56)
at framework.StatelessElement.new.performRebuild (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:25388:22)
at framework.StatelessElement.new.rebuild (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:21410:12)
at framework.StatelessElement.new.[_firstBuild] (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:25378:12)
at framework.StatelessElement.new.mount (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:25374:26)
at RenderObjectToWidgetElement.new.inflateWidget (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:21049:18)
at RenderObjectToWidgetElement.new.updateChild (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:20898:25)
at RenderObjectToWidgetElement.new.[_rebuild] (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:56356:37)
at RenderObjectToWidgetElement.new.mount (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:56336:29)
at http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:56272:37
at framework.BuildOwner.new.buildScope (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:53359:13)
at RenderObjectToWidgetAdapter.new.attachToRenderTree (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:56271:17)
at binding$5.WidgetsFlutterBinding.new.attachRootWidget (http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:56126:236)
at http://localhost:50156/packages/flutter/src/widgets/title.dart.lib.js:56120:14
    at internalCallback (http://localhost:50156/dart_sdk.js:26685:11)

It works fine on Chrome on Windows, but on my mac it breaks.

[EDIT] I'm using Flutter 3.0.0, Hive v2.2.0

@TheOneWithTheBraid
Copy link

I think this can be resolved by calling Hive.init(null) before accessing any other Hive API. I will adjust the HiveImpl in order to make this optional again by providing a default BackendManager in case Hive.init is not called.

@zgramming
Copy link

@TheOneWithTheBraid I used Hive.initFlutter() to initialize Hive before application start, Hive.init(null) can resolved error on web, but when use Hive.init(null) in desktop application (Windows), it throw error :

[ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: HiveError: You need to initialize Hive or provide a path to store the box.
#0      BackendManager.open
package:hive/…/vm/backend_manager.dart:21
#1      HiveImpl._openBox
package:hive/src/hive_impl.dart:106
#2      HiveImpl.openBox
package:hive/src/hive_impl.dart:146
#3      _initHive
package:funfly/main.dart:21
#4      main
package:funfly/main.dart:26

For now, i think safe way is downgrade to version 2.1.0 before this bug have resolved.

TheOneWithTheBraid pushed a commit that referenced this issue May 18, 2022
- remove need for manual `Hive.init()` call
- provide default backend manager
- fix most tests

Fixes: #967

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
@TheOneWithTheBraid
Copy link

Ah, I see the problem you face: For some reasons, the hive_flutter package wasn't updated on pub.dev even though an update is internally required. @leisim

As of now, you can override is by git:

dependency_overrides:
  hive:
    git:
      url: https://github.com/hivedb/hive.git
      path: hive
      ref: braid/backend-manager-fallback
  hive_flutter:
    git:
      url: https://github.com/hivedb/hive.git
      path: hive_flutter
      ref: braid/backend-manager-fallback

TheOneWithTheBraid pushed a commit to TheOneWithTheBraid/hive that referenced this issue May 18, 2022
- remove need for manual `Hive.init()` call
- provide default backend manager
- fix most tests

Fixes: isar#967

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
@sgehrman
Copy link

2.2.0 needs to be pulled. It's a disaster.

roubachof pushed a commit to roubachof/hive that referenced this issue May 24, 2022
- remove need for manual `Hive.init()` call
- provide default backend manager
- fix most tests

Fixes: isar#967

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
problem An unconfirmed bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants