Skip to content

fix(*): translate native compress failures to CompressError instead of null - #397

Merged
AlexV525 merged 3 commits into
mainfrom
fix/compress-error-propagation
Jul 16, 2026
Merged

fix(*): translate native compress failures to CompressError instead of null#397
AlexV525 merged 3 commits into
mainfrom
fix/compress-error-propagation

Conversation

@AlexV525

Copy link
Copy Markdown
Member

Summary

  • Native compress failures on Android/iOS/macOS previously reported back as reply(null) / result(nil), forcing Dart callers to bare null-checks with no diagnostic — and creating a latent TypeError on compressWithList (common), whose signature is non-nullable but which returned the native null unchanged.
  • Native handlers now emit stable failure codes (unsupported_format, decode_failed, encode_failed, io_failed, unknown) via replyError / FlutterError; Dart platform impls catch PlatformException and rethrow CompressError(message, code: ...).
  • Public signatures unchanged — this is intentionally non-breaking. Callers who already catch CompressError (documented in the platform interface) now get a real error kind + message; callers doing result == null still compile.
  • Also removes 3 unreachable if (!support) return null; branches in common (checkSupportPlatform already throws UnsupportedError), and adds a defensive null guard to compressWithList (common) matching macOS/ohos.

Related issues

Wire protocol

Failure codes emitted by the native side (documented on CompressError.code):

Code Meaning
unsupported_format No handler for the requested CompressFormat
decode_failed Could not decode the input (unreadable image / unknown container)
encode_failed Encoder returned no data (includes HEIF hardware-encoder unavailability)
io_failed File read/write/mkdir failed
unknown Everything else (message carries the reason)

Test plan

  • melos run analyze clean across all 7 packages
  • iOS integration smoke (melos run verify_ios_behavior) — golden path unchanged, only failure paths touched
  • macOS integration smoke (melos run verify_macos_behavior)
  • Android manual repro: pass random-noise bytes to compressWithList on an Android device; before this change, BitmapFactory.decodeByteArray returning null caused a Dart TypeError (non-nullable Uint8List = null). After this change, callers receive CompressError(code: 'unknown', message: '...').
  • iOS manual repro: call compressWithFile with a non-image file — before: result == null with no info. After: CompressError(code: 'decode_failed', message: 'Input file is not a decodable image (mime=application/octet-stream, path=...)').

Note: the repo has no test/ unit-test scaffolding today — behavioral coverage lives in packages/flutter_image_compress/example/integration_test/compress_test.dart. Adding unit tests for the _invoke translator would require setting up flutter_test + test/ in three platform packages, which is a bigger separate change; happy to add it if reviewers prefer.

🤖 Generated with Claude Code

AlexV525 and others added 2 commits July 16, 2026 16:19
Introduce an optional `code` field on `CompressError` so callers can
branch on failure kind without string-matching the message. Stable
values emitted by the native side: `unsupported_format`,
`decode_failed`, `encode_failed`, `io_failed`, `unknown`. Unknown codes
pass through verbatim for forward-compat.

Additive change — `code` is nullable and the existing single-arg
constructor is unchanged.

Refs #252

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…f null

Native compress failures on Android/iOS/macOS previously reported back
to Dart as `reply(null)` / `result(nil)`, forcing callers to do a bare
null-check with no diagnostic — and creating a latent `TypeError` on
`compressWithList` (common), whose signature is non-nullable but which
happily returned the native null unchanged.

Native side (Android Kotlin / iOS Objective-C / macOS Swift):
  - `reply(null)` / `result(nil)` on the failure path becomes
    `replyError(code, msg)` / `result([FlutterError ...])` /
    `FlutterError(code:..., message:...)` with a stable code:
    `unsupported_format`, `decode_failed`, `encode_failed`, `io_failed`,
    `unknown`.
  - iOS `handleCompressFileToFile:` also translates `writeToURL:`
    failure from a silent `result(nil)` to `io_failed` with the target
    path in the message.

Dart side (common / macOS / ohos):
  - Introduce a private `_invoke<T>` helper on each platform impl that
    catches `PlatformException` and rethrows `CompressError(msg, code:
    ...)`. Every compress-related `_channel.invokeMethod` call now
    goes through it.
  - Delete three unreachable `if (!support) return null;` branches in
    common — `FlutterImageCompressValidator.checkSupportPlatform`
    already throws `UnsupportedError` when a format is not supported;
    it never actually returns `false`.
  - Add a defensive `if (result == null) throw CompressError(...)` in
    `compressWithList` (common), aligning with the guard macOS and ohos
    already had. This closes the latent `TypeError` — the native side
    should no longer emit a `success(null)` on the failure path, but
    the guard keeps the non-nullable return type honest.
  - Replace `throw Exception('Compress failed')` in macOS and ohos
    `compressWithList` with `throw CompressError('Compress failed')`
    for consistency.

Existing null-checking callers still compile — public signatures
unchanged. Callers who already catch `CompressError` now get a real
error kind + message instead of an opaque null return.

Refs #252, refs #396

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AlexV525
AlexV525 requested a review from CaiJingLong as a code owner July 16, 2026 08:21
Re-wrap CompressError.toString ternary to match `dart format` output.
No behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AlexV525
AlexV525 merged commit 296af9a into main Jul 16, 2026
9 checks passed
@AlexV525
AlexV525 deleted the fix/compress-error-propagation branch July 16, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant