# Cannot access field on a value with type ARRAY > You wrote array.field, but a dot only reaches into a STRUCT, and the column is a repeated one. The message prints the array type, which shows what to unnest. This is the error GA4 and Universal Analytics exports produce for hits.page and event_params.value. Source: https://docs.querylab.io/bigquery-errors/cannot-access-field-on-a-value-with-type-array/ Updated: 2026-10-30 ```text Cannot access field a on a value with type ARRAY> at [2:14] ``` ## What BigQuery means A dot reaches into a `STRUCT`. The column in front of the dot is an `ARRAY`, which is a list of structs, and a list has no fields. BigQuery will not pick an element for you or map the field over every element. The type in the message is the useful part. Read it from the outside in: - `ARRAY>` is a repeated record. Each row holds zero or more structs. - The names inside `STRUCT<...>` are the fields you can reach after you have one struct in hand. - The position points at the field name after the dot. It does not point at the array. - A struct with more than three fields is cut off after the third, and `...` stands for the rest. At every nesting level the message shows at most three fields. The error names the first array on the path. In `hits.page.pagePath`, the message complains about `page`, because `hits` is the array. Fix that one and the next dot works. ## The smallest query that reproduces it ```sql WITH t AS (SELECT [STRUCT(1 AS a), STRUCT(2 AS a)] AS items) SELECT items.a FROM t ``` `items` has two structs in one row. `items.a` would have to be two values, and a column cannot hold that. ## The fix You have three ways out, depending on what you want. **One output row per element.** Unnest the array in `FROM`. This is the answer most of the time: ```sql SELECT item.a FROM t, UNNEST(t.items) AS item ``` The comma is a cross join with each row's own array, so rows with an empty array disappear. Use `LEFT JOIN UNNEST(t.items) AS item` to keep them with a `NULL`. BigQuery also accepts the shorthand `FROM t, t.items AS item`. **One element, picked by position.** Subscript the array, then use the dot: ```sql SELECT items[OFFSET(0)].a AS first_a FROM t ``` `OFFSET(0)` fails when the array is shorter. `SAFE_OFFSET(0)` gives `NULL` instead. A bare `items[0]` also works and means the same as `OFFSET(0)`. **A value computed over the elements.** Keep one row per source row and use a subquery: ```sql SELECT (SELECT SUM(i.a) FROM UNNEST(items) AS i) AS total, ARRAY(SELECT i.a FROM UNNEST(items) AS i) AS all_a FROM t ``` This avoids the row multiplication of a join, so it is also the safe choice when you are already aggregating at the row level. `EXISTS (SELECT 1 FROM UNNEST(items) AS i WHERE i.a = 2)` filters rows by a condition on any element. ## GA4 and Universal Analytics exports Most people meet this on exports where whole tables are built from repeated records. Universal Analytics sessions keep every page view in `hits`, and each hit has a `page` struct: ```sql WITH s AS ( SELECT 'v1' AS id, [STRUCT(STRUCT('/home' AS pagePath) AS page, 1 AS hitNumber), STRUCT(STRUCT('/cart' AS pagePath) AS page, 2 AS hitNumber)] AS hits ) SELECT id, hit.page.pagePath FROM s, UNNEST(hits) AS hit ``` Writing `hits.page.pagePath` on the real sample table `bigquery-public-data.google_analytics_sample.ga_sessions_20170801` fails with `Cannot access field page on a value with type ARRAY> at [1:13]`. Only `hits` is an array. After `UNNEST`, `hit.page` is a plain struct and the second dot is fine. GA4 stores event parameters as `ARRAY>>`. Writing `event_params.value.string_value` on `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_20210131` fails with `Cannot access field value on a value with type ARRAY>> at [1:21]`. Pick the parameter by key: ```sql SELECT (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page FROM e ``` `user_properties` has the same key and value shape. `items` is a plain repeated record, so unnest it: `items.item_id` fails with `Cannot access field item_id on a value with type ARRAY> at [1:14]`. ## The variants of the message The text changes with the type that was in front of the dot. | Message | Cause | | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | `Cannot access field a on a value with type ARRAY>` | a dot on an array of structs | | `Cannot access field a on a value with type ARRAY` | a dot on an array of plain values | | `Cannot access field value on a value with type ARRAY>>` | GA4 `event_params.value` | | `Cannot access field a on a value with type INT64` | a dot on a column that is not a struct at all | | `Cannot access field a on a value with type STRING` | the same for a string (see JSON below) | The error appears wherever the expression is: the `SELECT` list, `WHERE`, `ORDER BY` and `ON` clauses all report it, with the position of the field name. A field that does not exist is not reported by this error. Against a plain struct, BigQuery says `Field name b does not exist in STRUCT`. Against an array, a wrong field name still gives `Cannot access field`, so check the array first and the spelling second. ## Queries that look wrong but run **Naming the unnest alias like the column.** The alias wins inside the query: ```sql SELECT items.a FROM t, UNNEST(items) AS items ``` It works, but `items.a` now means the unnested struct, not the original column. Rename the alias to the singular, as in `item`, so you can still see both. **A dot on an array element.** Once you have one struct, any depth of dots is fine: ```sql SELECT item.inner_s.z FROM t, UNNEST(items) AS item ``` ## Different errors that look like this one **`Field name b does not exist in STRUCT`.** The column is a struct and the field name is wrong. **`Dot-star is not supported for type ARRAY>`.** `items.*` has the same cause as this page. Unnest first, then `item.*`. **`Values referenced in UNNEST must be arrays. UNNEST contains expression of type INT64`.** The opposite mistake: you unnested something that is not an array. **`Equality is not defined for arguments of type ARRAY`.** You compared a whole array with `=`. See [No matching signature for operator](https://docs.querylab.io/bigquery-errors/no-matching-signature-for-operator/). If the column is `JSON`, a dot reaches into it, and that is a different feature. A `STRING` that holds JSON needs `PARSE_JSON` first. Writing `s.a` on a `STRING` gives the `STRING` row in the table above. ## In Querylab.io The editor's own checks do not mark this one as you type. It is a compile error, so BigQuery's dry run reports it without reading data, and Querylab.io shows dry-run errors when you pause typing. The message is BigQuery's own, with the array type cut off after three fields. The failed-run panel has no one-click fix for it. Autocomplete does help while you write the query: after `item.` on an unnested alias it offers the struct's fields. See [Arrays](https://docs.querylab.io/bigquery/arrays/) and [Structs](https://docs.querylab.io/bigquery/structs/). ## Sources Based on the BigQuery documentation by Google, CC BY 4.0, rewritten with examples checked against BigQuery: - [Operators, field access and array subscript](https://cloud.google.com/bigquery/docs/reference/standard-sql/operators) - [Query syntax, UNNEST operator](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax) - [Work with arrays](https://cloud.google.com/bigquery/docs/arrays) - [Nested and repeated fields](https://cloud.google.com/bigquery/docs/best-practices-performance-nested)