Skip to content

Change causing a crash while image decoding #164944

Closed

Description

@apwilson

Steps to reproduce

The following cl seems to be causing a frequent null check crash in my app:

8023527

UnknownError: Null check operator used on a null value
at MultiFrameImageStreamCompleter._decodeNextFrameAndSchedule (image_stream.dart:1133)

Sometimes the code line is 1111 depending on the version of flutter I have. That points to the _codec vairable being null checked before being disposed here:

The crash happens very frequently. Maybe we can revert this commit?

Expected results

Don't crash due to a null check.

Actual results

It crashes.

Code sample

n/a

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]

Activity

  1. jonahwilliams commented on Mar 10, 2025

    @jonahwilliams
    Contributor

    The codec_ variable is null checked a few lines above that. Furthermore that line is inside a block that acceess a property on codec. Seems like that would crash first?

    if (_codec == null) {
    // codec was disposed during getNextFrame
    return;
    }
    if (_codec!.frameCount == 1) {
    // ImageStreamCompleter listeners removed while waiting for next frame to
    // be decoded.
    // There's no reason to emit the frame without active listeners.
    if (!hasListeners) {
    return;
    }
    // This is not an animated image, just return it and don't schedule more
    // frames.
    _emitFrame(
    ImageInfo(image: _nextFrame!.image.clone(), scale: _scale, debugLabel: debugLabel),
    );
    _nextFrame!.image.dispose();
    _nextFrame = null;
    _codec!.dispose();

  2. apwilson commented on Mar 10, 2025

    @apwilson
    ContributorAuthor

    I believe the _emitFrame call here must have a side effect that occasionally causes the _codec to be disposed - maybe the receiver of the frame disposes the codec for some reason in certain circumstances? I could conceive of that calling some callback in client code which in-turn calls dispose on the image stream or something.

    It's not if it can happen. It definitely can happening - it's happening now.

    The code should protect itself by using a ? instead of a ! or check for null first before attempting to dispose.

    Note also I see it doing this null check on _codec in two places so we probably want to protect that second location too.

  3. jonahwilliams commented on Mar 10, 2025

    @jonahwilliams
    Contributor

    Yeah that makes sense. THe listeners run immediately so one could decide to dispose the image. So just changing the final !.dispose to a ?dispose should be safe.

  4. jason-simmons commented on Mar 11, 2025

    @jason-simmons
    Contributor

    #164537 has a way to reproduce this in Wonderous.

    Captured a stack trace from Wonderous:

    #0      MultiFrameImageStreamCompleter._maybeDispose (package:flutter/src/painting/image_stream.dart:1160:95)
    #1      ImageStreamCompleter.removeListener (package:flutter/src/painting/image_stream.dart:659:7)
    #2      MultiFrameImageStreamCompleter.removeListener (package:flutter/src/painting/image_stream.dart:1144:11)
    #3      RetryImage.loadImage.<anonymous closure> (package:wonders/logic/common/retry_image.dart:82:23)
    #4      ImageStreamCompleter.removeListener (package:flutter/src/painting/image_stream.dart:656:17)
    #5      _PendingImage.removeListener (package:flutter/src/painting/image_cache.dart:652:15)
    #6      ImageCache.putIfAbsent.listener (package:flutter/src/painting/image_cache.dart:424:22)
    #7      ImageStreamCompleter.setImage (package:flutter/src/painting/image_stream.dart:745:25)
    #8      _DelegatingImageStreamCompleter.addImage (package:wonders/logic/common/retry_image.dart:106:5)
    #9      RetryImage.loadImage.<anonymous closure> (package:wonders/logic/common/retry_image.dart:62:19)
    #10     ImageStreamCompleter.setImage (package:flutter/src/painting/image_stream.dart:745:25)
    #11     MultiFrameImageStreamCompleter._emitFrame (package:flutter/src/painting/image_stream.dart:1130:5)
    #12     MultiFrameImageStreamCompleter._decodeNextFrameAndSchedule (package:flutter/src/painting/image_stream.dart:1107:7)
    

    _emitFrame calls setImage, which invokes listener onImage callbacks. The callback may remove the last listener, resulting in a call to _maybeDispose which clears the _codec.

    So adding the _codec null check after the _emitFrame call would solve this. I'll create a PR.

  5. github-actions commented on Mar 26, 2025

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  6. locked as resolved and limited conversation to collaborators on Mar 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    c: crashStack traces logged to the consoleteam-frameworkOwned by Framework team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions