Skip to content

[flutter_tools] Report malformed plugin platform entries instead of crashing - #193151

Merged
auto-submit[bot] merged 5 commits into
flutter:masterfrom
ishaquehassan:fix/162150-plugin-yaml-non-string-values
Oct 8, 2026
Merged

auto-submit[bot] merged 5 commits into
flutter:masterfrom
ishaquehassan:fix/162150-plugin-yaml-non-string-values

Conversation

@ishaquehassan

Copy link
Copy Markdown
Contributor

A platforms: entry that declares default_package: with no value makes the tool crash with _TypeError: type 'Null' is not a subtype of type 'String' in type cast out of Plugin._getDefaultPackageForPlatform, rather than telling the user which part of the pubspec is wrong.

Plugin.fromYaml validates before constructing, and the private constructors document that they rely on that, so _getDefaultPackageForPlatform and _getPluginDartClassForPlatform cast straight to String. The gap is in _validateMultiPlatformYaml: its isInvalid helper returns early as soon as a platform entry contains default_package, so neither that key's type nor the rest of the entry is ever checked. The per-platform validate methods do test yaml[kDefaultPackage] is String, but those are exactly the ones being skipped.

isInvalid now rejects a platform entry that declares default_package, dartPluginClass or dartFileName with a non-string value, the three per-platform keys that are cast to String while the plugin is built. A malformed pubspec gets the existing Invalid "android" plugin specification. tool exit instead of a crash. Entries that declare those keys as strings, or leave them out, take the same path as before, and no platform validator changed.

Two regression tests in packages/flutter_tools/test/general.shard/plugins_test.dart cover one cast site each, alongside the other Plugin.fromYaml rejects ... cases. Both fail without the change with the type error above.

Fixes #162150

Pre-launch Checklist

…rashing

A platform entry under `platforms:` that declares `default_package`,
`dartPluginClass` or `dartFileName` with a non-string value skipped
validation and reached a `String` cast while the plugin was built, so a
malformed pubspec crashed the tool with a type error. Reject those
entries in `_validateMultiPlatformYaml` so the existing invalid
specification tool exit is reported instead.
@github-actions github-actions Bot added the tool Affects the "flutter" command-line tool. See also t: labels. label Sep 22, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds validation to reject non-string values for default_package, dartPluginClass, and dartFileName in plugin specifications, preventing tool crashes due to type errors. It also includes regression tests for default_package and dartPluginClass. The feedback suggests adding a test case for dartFileName to ensure complete test coverage of the new validation logic.

Comment on lines +2297 to +2316
testUsingContext('Plugin.fromYaml rejects a non-string dartPluginClass', () async {
const malformedYaml = '''
platforms:
linux:
pluginClass: SamplePlugin
dartPluginClass:
''';
expect(
() => Plugin.fromYaml(
'malformed_plugin',
'',
loadYaml(malformedYaml) as YamlMap,
null,
const <String>[],
fileSystem: globals.fs,
isDevDependency: false,
),
throwsToolExit(message: 'Invalid "linux" plugin specification.'),
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While default_package and dartPluginClass are covered by the new regression tests, dartFileName is also checked by _hasNonStringValue but lacks a corresponding test. Adding a test case for dartFileName ensures complete coverage of the new validation logic.

      testUsingContext('Plugin.fromYaml rejects a non-string dartPluginClass', () async {
        const malformedYaml = '''
platforms:
  linux:
    pluginClass: SamplePlugin
    dartPluginClass:
''';
        expect(
          () => Plugin.fromYaml(
            'malformed_plugin',
            '',
            loadYaml(malformedYaml) as YamlMap,
            null,
            const <String>[],
            fileSystem: globals.fs,
            isDevDependency: false,
          ),
          throwsToolExit(message: 'Invalid "linux" plugin specification.'),
        );
      });

      testUsingContext('Plugin.fromYaml rejects a non-string dartFileName', () async {
        const malformedYaml = '''
platforms:
  windows:
    pluginClass: SamplePlugin
    dartFileName:
''';
        expect(
          () => Plugin.fromYaml(
            'malformed_plugin',
            '',
            loadYaml(malformedYaml) as YamlMap,
            null,
            const <String>[],
            fileSystem: globals.fs,
            isDevDependency: false,
          ),
          throwsToolExit(message: 'Invalid "windows" plugin specification.'),
        );
      });
References
  1. Code should be tested and follow the guidance described in the writing effective tests guide. (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a dartFileName test, though not the empty one. An empty dartFileName: never crashed: that key is read with as String? and falls back to <plugin>.dart, so rejecting it would have broken plugins that work today. What does crash is a value of the wrong type (dartFileName: 123), since that still reaches the cast. The check now leaves null alone for this key and rejects only that, and there are two tests: the wrong type is reported, and the empty entry still resolves to the default file name 👌

The non-string check was too broad for dartFileName. Unlike default_package
and dartPluginClass, which are read with a non-nullable `as String` cast and
so crash on an empty entry, dartFileName is read with `as String?` and falls
back to `<plugin>.dart` when null. An empty `dartFileName:` has therefore
always been valid, and rejecting it would have broken working plugins.

Only a wrong-typed value (`dartFileName: 123`) reaches the cast and crashes,
so that is what is rejected now. Tests cover both: the wrong type is
reported, and the empty entry still resolves to the default file name.
@bkonyi bkonyi added the CICD Run CI/CD label Sep 24, 2026
bkonyi
bkonyi previously approved these changes Sep 24, 2026

@bkonyi bkonyi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We'll just need one more review before this can land.

@bkonyi
bkonyi requested a review from chingjun September 24, 2026 20:45
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Sep 29, 2026
chingjun
chingjun previously approved these changes Oct 2, 2026
@chingjun chingjun added the CICD Run CI/CD label Oct 2, 2026
@bkonyi bkonyi added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 2, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 2, 2026
@auto-submit

auto-submit Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193151, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@bkonyi

bkonyi commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Hi @ishaquehassan, it looks like there's a formatting failure. Can you run dart format packages/flutter_tools/test/general.shard/plugins_test.dart and upload a new commit? Thanks!

@ishaquehassan
ishaquehassan dismissed stale reviews from chingjun and bkonyi via f1bbe9c October 8, 2026 08:21
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 8, 2026
@ishaquehassan

ishaquehassan commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

done @bkonyi, ran dart format and pushed 👍 looks like the new commit dismissed the approvals though, so would need a re-approve from you and @chingjun whenever you get a chance

@bkonyi bkonyi added the CICD Run CI/CD label Oct 8, 2026
@bkonyi bkonyi added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 8, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 8, 2026
@auto-submit

auto-submit Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193151, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR.

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 8, 2026
@bkonyi bkonyi added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels Oct 8, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Oct 8, 2026
Merged via the queue into flutter:master with commit 9e4f28b Oct 8, 2026
29 of 30 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[tool_crash] _TypeError: (#0 Plugin._getDefaultPackageForPlatform (package:flutter_tools/src/plugins.dart:351:71))

3 participants