Repository: pantsbuild/pants

Issue:
Misleading errror message for parametrized field that is unknown
When using field parametrization it is required that the field be movable, and Pants presents an error message if it is not, which is good. However if the field is not movable due to not being a valid field at all, this becomes misleading as the proper error message is to say that this is not a valid field, regardless of parametrization or not.

```
11:56:51.44 [ERROR] 1 Exception encountered:

  InvalidFieldException: Only fields which will be moved to generated targets may be parametrized, so target generator src/python/... (with type python_sources) cannot parametrize the {'foobar'} field.
```



Discussion:

Resolving patch:
diff --git a/src/python/pants/engine/internals/graph.py b/src/python/pants/engine/internals/graph.py
--- a/src/python/pants/engine/internals/graph.py
+++ b/src/python/pants/engine/internals/graph.py
@@ -218,15 +218,23 @@ async def resolve_target_parametrizations(
                 if field_value is not None:
                     template_fields[field_type.alias] = field_value
 
+        field_type_aliases = target_type._get_field_aliases_to_field_types(
+            target_type.class_field_types(union_membership)
+        ).keys()
         generator_fields_parametrized = {
-            name for name, field in generator_fields.items() if isinstance(field, Parametrize)
+            name
+            for name, field in generator_fields.items()
+            if isinstance(field, Parametrize) and name in field_type_aliases
         }
         if generator_fields_parametrized:
             noun = pluralize(len(generator_fields_parametrized), "field", include_count=False)
+            generator_fields_parametrized_text = ", ".join(
+                repr(f) for f in generator_fields_parametrized
+            )
             raise InvalidFieldException(
                 f"Only fields which will be moved to generated targets may be parametrized, "
                 f"so target generator {address} (with type {target_type.alias}) cannot "
-                f"parametrize the {generator_fields_parametrized} {noun}."
+                f"parametrize the {generator_fields_parametrized_text} {noun}."
             )
 
         base_generator = target_type(
diff --git a/src/python/pants/engine/internals/parametrize.py b/src/python/pants/engine/internals/parametrize.py
--- a/src/python/pants/engine/internals/parametrize.py
+++ b/src/python/pants/engine/internals/parametrize.py
@@ -115,7 +115,7 @@ def expand(
     def __repr__(self) -> str:
         strs = [str(s) for s in self.args]
         strs.extend(f"{alias}={value}" for alias, value in self.kwargs.items())
-        return f"parametrize({', '.join(strs)}"
+        return f"parametrize({', '.join(strs)})"
 
 
 @dataclass(frozen=True)


Test patch:
diff --git a/src/python/pants/engine/internals/graph_test.py b/src/python/pants/engine/internals/graph_test.py
--- a/src/python/pants/engine/internals/graph_test.py
+++ b/src/python/pants/engine/internals/graph_test.py
@@ -49,6 +49,7 @@
     HydrateSourcesRequest,
     InferDependenciesRequest,
     InferredDependencies,
+    InvalidFieldException,
     MultipleSourcesField,
     OverridesField,
     SecondaryOwnerMixin,
@@ -1347,6 +1348,23 @@ class ChildTarget(Target):
     )
 
 
+@pytest.mark.parametrize(
+    "field_content",
+    [
+        "tagz=['tag']",
+        "tagz=parametrize(['tag1'], ['tag2'])",
+    ],
+)
+def test_parametrize_16910(generated_targets_rule_runner: RuleRunner, field_content: str) -> None:
+    with engine_error(InvalidFieldException, contains=f"Unrecognized field `{field_content}`"):
+        assert_generated(
+            generated_targets_rule_runner,
+            Address("demo"),
+            f"generator({field_content}, sources=['*.ext'])",
+            ["f1.ext", "f2.ext"],
+        )
+
+
 # -----------------------------------------------------------------------------------------------
 # Test `SourcesField`. Also see `engine/target_test.py`.
 # -----------------------------------------------------------------------------------------------