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

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 <[email protected]>
@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 <[email protected]>
@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 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
problem An unconfirmed bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants