Repository: scikit-hep/awkward

Issue:
ak.unflatten on a nested IndexedOptionArray typetracer hits a bug trying to calculate lengths
### Version of Awkward Array

2.5.1

### Description and code to reproduce

```python3
import json
import awkward as ak

fromjson = {
    "class": "ListOffsetArray",
    "offsets": "i64",
    "content": {
        "class": "ListOffsetArray",
        "offsets": "i64",
        "content": {
            "class": "IndexedOptionArray",
            "index": "i64",
            "content": {
                "class": "RecordArray",
                "fields": [
                    "pt",
                ],
                "contents": [
                    {
                        "class": "NumpyArray",
                        "primitive": "float32",
                        "inner_shape": [],
                        "parameters": {"__doc__": "pt"},
                        "form_key": None,
                    },
                ],
                "parameters": {
                    "__record__": "PFCand",
                    "__doc__": "interesting particles from various jet collections",
                    "collection_name": "PFCands",
                },
                "form_key": None,
            },
            "parameters": {},
            "form_key": None,
        },
        "parameters": {},
        "form_key": None,
    },
    "parameters": {},
    "form_key": None,
}

form = ak.forms.from_json(json.dumps(fromjson))

ttlayout, report = ak.typetracer.typetracer_with_report(form)

ttarray = ak.Array(ttlayout, behavior=ak.behavior)

flattened = ak.flatten(ttarray, axis=2)

counts = ak.flatten(ak.num(ttarray, axis=2), axis=1)

unflattened = ak.unflatten(flattened, counts, axis=1)
```
Yields the following error:
```
Traceback (most recent call last):
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/_dispatch.py", line 62, in dispatch
    next(gen_or_result)
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/operations/ak_unflatten.py", line 90, in unflatten
    return _impl(array, counts, axis, highlevel, behavior, attrs)
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/operations/ak_unflatten.py", line 98, in _impl
    ctx.unwrap(array, allow_record=False, primitive_policy="error").to_packed(),
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/contents/listoffsetarray.py", line 2219, in to_packed
    next_content = next._content[: next._offsets[-1]].to_packed()
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/contents/indexedoptionarray.py", line 1725, in to_packed
    len(original_index) - num_none
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/_nplikes/typetracer.py", line 320, in __len__
    raise AssertionError(
AssertionError: bug in Awkward Array: attempt to get length of a TypeTracerArray

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "form_madness16.py", line 54, in <module>
    unflattened = ak.unflatten(flattened, counts, axis=1)
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/_dispatch.py", line 70, in dispatch
    return gen_or_result
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/_errors.py", line 85, in __exit__
    self.handle_exception(exception_type, exception_value)
  File "/Users/lgray/miniforge3/envs/coffea-dev/lib/python3.8/site-packages/awkward/_errors.py", line 95, in handle_exception
    raise self.decorate_exception(cls, exception)
AssertionError: bug in Awkward Array: attempt to get length of a TypeTracerArray

See if this has been reported at https://github.com/scikit-hep/awkward/issues
```


Discussion:
@cmoore24-24

Resolving patch:
diff --git a/src/awkward/contents/indexedoptionarray.py b/src/awkward/contents/indexedoptionarray.py
--- a/src/awkward/contents/indexedoptionarray.py
+++ b/src/awkward/contents/indexedoptionarray.py
@@ -1718,27 +1718,26 @@ def continuation():
             raise AssertionError(result)
 
     def to_packed(self) -> Self:
-        original_index = self._index.raw(self._backend.nplike)
-        is_none = original_index < 0
-        num_none = self._backend.index_nplike.count_nonzero(is_none)
-        if self.parameter("__array__") == "categorical" or self._content.length <= (
-            len(original_index) - num_none
+        index_nplike = self._backend.index_nplike
+        is_none = self._index.data < 0
+        num_none = index_nplike.index_as_shape_item(index_nplike.count_nonzero(is_none))
+        if self.parameter("__array__") == "categorical" or (
+            self._backend.index_nplike.known_data
+            and self._content.length <= (self._index.length - num_none)
         ):
             return ak.contents.IndexedOptionArray(
                 self._index, self._content.to_packed(), parameters=self._parameters
             )
 
         else:
-            new_index = self._backend.index_nplike.empty(
-                len(original_index), dtype=original_index.dtype
-            )
+            new_index = index_nplike.empty(self._index.length, dtype=self._index.dtype)
             new_index[is_none] = -1
-            new_index[~is_none] = self._backend.index_nplike.arange(
-                len(original_index) - num_none,
-                dtype=original_index.dtype,
+            new_index[~is_none] = index_nplike.arange(
+                index_nplike.shape_item_as_index(new_index.size - num_none),
+                dtype=self._index.dtype,
             )
             return ak.contents.IndexedOptionArray(
-                ak.index.Index(new_index, nplike=self._backend.index_nplike),
+                ak.index.Index(new_index, nplike=index_nplike),
                 self.project().to_packed(),
                 parameters=self._parameters,
             )


Test patch: