Repository: scikit-hep/awkward

Issue:
ak.where unexpectedly fails on typetracers of option types
### Version of Awkward Array

2.7.1

### Description and code to reproduce

The following code:
```python3

import json
import awkward as ak

fromdict = {'class': 'RecordArray', 'fields': ['muon', 'jet'], 'contents': [{'class': 'ListOffsetArray', 'offsets': 'i64', 'content': {'class': 'RecordArray', 'fields': ['pt', 'eta', 'phi', 'crossref'], 'contents': [{'class': 'NumpyArray', 'pr\
imitive': 'int64', 'inner_shape': [], 'parameters': {}, 'form_key': "muon_pt!"}, {'class': 'NumpyArray', 'primitive': 'int64', 'inner_shape': [], 'parameters': {}, 'form_key': "muon_eta!"}, {'class': 'NumpyArray', 'primitive': 'int64', 'inner_\
shape': [], 'parameters': {}, 'form_key': "muon_phi!"}, {"class": "ListOffsetArray", "offsets": "i64", "content": {"class": "NumpyArray", "primitive": "int64", "inner_shape": [], "parameters": {}, "form_key": "muon_crossref_content!"}, "parame\
ters": {}, "form_key": "muon_crossref_index!"}], 'parameters': {}, 'form_key': "muon_record!"}, 'parameters': {}, 'form_key': "muon_list!"}, {'class': 'ListOffsetArray', 'offsets': 'i64', 'content': {'class': 'RecordArray', 'fields': ['pt', 'e\
ta', 'phi', "crossref", "thing1"], 'contents': [{'class': 'NumpyArray', 'primitive': 'int64', 'inner_shape': [], 'parameters': {}, 'form_key': "jet_pt!"}, {'class': 'NumpyArray', 'primitive': 'int64', 'inner_shape': [], 'parameters': {}, 'form\
_key': "jet_eta!"}, {'class': 'NumpyArray', 'primitive': 'int64', 'inner_shape': [], 'parameters': {}, 'form_key': "jet_phi!"}, {"class": "ListOffsetArray", "offsets": "i64", "content": {"class": "NumpyArray", "primitive": "int64", "inner_shap\
e": [], "parameters": {}, "form_key": "jet_crossref_content!"}, "parameters": {}, "form_key": "jet_crossref_index!"}, {'class': 'NumpyArray', 'primitive': 'int64', 'inner_shape': [], 'parameters': {}, 'form_key': "jet_thing1!"}], 'parameters':\
 {}, 'form_key': "jet_record!"}, 'parameters': {}, 'form_key': "jet_list!"}], 'parameters': {}, 'form_key': "outer!"}

form = ak.forms.from_dict(fromdict)

ttlayout, report = ak.typetracer.typetracer_with_report(form)

ttarray = ak.Array(ttlayout)

maybe = ak.firsts(ttarray)

varmaybe = ak.pad_none(ttarray, 3)

where = ak.where(abs(ttarray.jet.eta) < 1., 0.000511, ttarray.jet.thing1)

varmaybe_where = ak.where(abs(varmaybe.jet.eta) < 1., 0.000511, varmaybe.jet.thing1)

maybe_where = ak.where(abs(maybe.jet.eta) < 1., 0.000511, maybe.jet.thing1)
```

Fails with:
```
Traceback (most recent call last):
  File "/Users/lgray/coffea-dev/coffea/form_madness_again.py", line 18, in <module>
    varmaybe_where = ak.where(abs(varmaybe.jet.eta) < 1., 0.000511, varmaybe.jet.thing1)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_dispatch.py", line 64, in dispatch
    next(gen_or_result)
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/operations/ak_where.py", line 65, in where
    return _impl3(condition, x, y, mergebool, highlevel, behavior, attrs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/operations/ak_where.py", line 130, in _impl3
    out = ak._broadcasting.broadcast_and_apply(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1200, in broadcast_and_apply
    out = apply_step(
          ^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1178, in apply_step
    return continuation()
           ^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1147, in continuation
    return broadcast_any_list()
           ^^^^^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 671, in broadcast_any_list
    outcontent = apply_step(
                 ^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1178, in apply_step
    return continuation()
           ^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1147, in continuation
    return broadcast_any_list()
           ^^^^^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 747, in broadcast_any_list
    outcontent = apply_step(
                 ^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1178, in apply_step
    return continuation()
           ^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 1141, in continuation
    return broadcast_any_option_akwhere()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_broadcasting.py", line 863, in broadcast_any_option_akwhere
    NumpyArray(backend.nplike.zeros(len(inputs[2]), dtype=np.int8))
                                    ^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/contents/content.py", line 272, in __len__
    return int(self.length)
           ^^^^^^^^^^^^^^^^
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.12/site-packages/awkward/_nplikes/shape.py", line 79, in __int__
    raise TypeError("cannot interpret unknown lengths as concrete values")
TypeError: cannot interpret unknown lengths as concrete values

This error occurred while calling

    ak.where(
        <Array-typetracer [...] type='## * var * ?bool'>
        0.000511
        <Array-typetracer [...] type='## * var * ?int64'>
    )
```

Discussion:

Patch:
diff --git a/src/awkward/_broadcasting.py b/src/awkward/_broadcasting.py
index ae8f1f81..9c4b51c5 100644
--- a/src/awkward/_broadcasting.py
+++ b/src/awkward/_broadcasting.py
@@ -860,7 +860,7 @@ def apply_step(
             if not isinstance(xyc, Content):
                 unmasked.append(xyc)
                 masks.append(
-                    NumpyArray(backend.nplike.zeros(len(inputs[2]), dtype=np.int8))
+                    NumpyArray(backend.nplike.zeros(inputs[2].length, dtype=np.int8))
                 )
             elif not xyc.is_option:
                 unmasked.append(xyc)


Test patch:
diff --git a/tests/test_3321_akwhere_typetracer_lengths_optiontypes.py b/tests/test_3321_akwhere_typetracer_lengths_optiontypes.py
new file mode 100644
index 00000000..6c7e2343
--- /dev/null
+++ b/tests/test_3321_akwhere_typetracer_lengths_optiontypes.py
@@ -0,0 +1,139 @@
+from __future__ import annotations
+
+import awkward as ak
+
+fromdict = {
+    "class": "RecordArray",
+    "fields": ["muon", "jet"],
+    "contents": [
+        {
+            "class": "ListOffsetArray",
+            "offsets": "i64",
+            "content": {
+                "class": "RecordArray",
+                "fields": ["pt", "eta", "phi", "crossref"],
+                "contents": [
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "muon_pt!",
+                    },
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "muon_eta!",
+                    },
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "muon_phi!",
+                    },
+                    {
+                        "class": "ListOffsetArray",
+                        "offsets": "i64",
+                        "content": {
+                            "class": "NumpyArray",
+                            "primitive": "int64",
+                            "inner_shape": [],
+                            "parameters": {},
+                            "form_key": "muon_crossref_content!",
+                        },
+                        "parameters": {},
+                        "form_key": "muon_crossref_index!",
+                    },
+                ],
+                "parameters": {},
+                "form_key": "muon_record!",
+            },
+            "parameters": {},
+            "form_key": "muon_list!",
+        },
+        {
+            "class": "ListOffsetArray",
+            "offsets": "i64",
+            "content": {
+                "class": "RecordArray",
+                "fields": [
+                    "pt",
+                    "eta",
+                    "phi",
+                    "crossref",
+                    "thing1",
+                ],
+                "contents": [
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "jet_pt!",
+                    },
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "jet_eta!",
+                    },
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "jet_phi!",
+                    },
+                    {
+                        "class": "ListOffsetArray",
+                        "offsets": "i64",
+                        "content": {
+                            "class": "NumpyArray",
+                            "primitive": "int64",
+                            "inner_shape": [],
+                            "parameters": {},
+                            "form_key": "jet_crossref_content!",
+                        },
+                        "parameters": {},
+                        "form_key": "jet_crossref_index!",
+                    },
+                    {
+                        "class": "NumpyArray",
+                        "primitive": "int64",
+                        "inner_shape": [],
+                        "parameters": {},
+                        "form_key": "jet_thing1!",
+                    },
+                ],
+                "parameters": {},
+                "form_key": "jet_record!",
+            },
+            "parameters": {},
+            "form_key": "jet_list!",
+        },
+    ],
+    "parameters": {},
+    "form_key": "outer!",
+}
+
+form = ak.forms.from_dict(fromdict)
+ttlayout, report = ak.typetracer.typetracer_with_report(form)
+ttarray = ak.Array(ttlayout)
+
+
+def test_where():
+    ak.where(abs(ttarray.jet.eta) < 1.0, 0.000511, ttarray.jet.thing1)
+
+
+def test_maybe_where():
+    maybe = ak.firsts(ttarray)
+    ak.where(abs(maybe.jet.eta) < 1.0, 0.000511, maybe.jet.thing1)
+
+
+def test_varmaybe_where():
+    varmaybe = ak.pad_none(ttarray, 3)
+    ak.where(abs(varmaybe.jet.eta) < 1.0, 0.000511, varmaybe.jet.thing1)