GA4 “Unexpected Field(s)” Error: Windsor Field Names vs GA4 API Names
If you request Google Analytics 4 data through Windsor.ai and see “Unexpected field(s): {total_users}. Please check the supported field list (https://windsor.ai/data-field/googleanalytics4/)”, the request used a field name that does not exist in Windsor’s GA4 connector. Windsor checks names before calling Google, so nothing was sent and nothing is wrong with your property.
This is the most common GA4 error we see, and by a wide margin the most common one AI assistants cause: they type the GA4 API name (totalUsers), a snake_case version of it (total_users), or the label from the GA4 interface (Views), where Windsor expects its own field ID. The fix is a lookup, and the table below covers the names that come up most.
Why this happens
Windsor gives every connector its own flat set of field IDs so the same request format works across 350+ sources. For GA4 the IDs are mostly snake_case versions of Google’s API names (screen_page_views, event_count, session_source_medium), but a handful of the oldest fields are lowercase with no underscore (totalusers, newusers, totalrevenue, pagetitle, transactionid), and a few carry a different word altogether (conversions is Google’s keyEvents; source is sessionSource). Google’s camelCase names are never accepted as-is.
There is no fuzzy matching. total_users, totalUsers and Total users all fail; only totalusers works. When several names fail in one request the error lists all of them in the braces, so fix every name before retrying.
The complete list is in the Google Analytics 4 field reference. Through the Windsor MCP connector, the get_fields tool returns the same list, and Claude or ChatGPT should call it before the first data request rather than guess.
The names people guess most, and the Windsor field to use
Ranked by how often each wrong name appears in GA4 requests. Where two Windsor IDs are listed, both work and return the same data.
| You typed | Use this Windsor field | GA4 API name behind it |
|---|---|---|
| total_users, totalUsers | totalusers | totalUsers |
| new_users, newUsers | newusers | newUsers |
| device_category, deviceCategory | devicecategory | deviceCategory |
| session_source, sessionSource | source | sessionSource |
| session_medium, sessionMedium | medium | sessionMedium |
| sessionDefaultChannelGroup, session_default_channel_grouping, default_channel_grouping, channel, channel_group | session_default_channel_group | sessionDefaultChannelGroup |
| session_campaign_name, sessionCampaignName, session_campaign | campaign or campaign_name | sessionCampaignName |
| sessionSourceMedium | session_source_medium | sessionSourceMedium |
| screenPageViews, pageviews, page_views, views | screen_page_views | screenPageViews |
| eventCount, total_events | event_count | eventCount |
| eventName | event_name | eventName |
| total_revenue, revenue | totalrevenue (or transactionrevenue) | totalRevenue |
| purchaseRevenue | purchase_revenue | purchaseRevenue |
| engagementRate | engagement_rate | engagementRate |
| engagedSessions | engaged_sessions | engagedSessions |
| averageSessionDuration, avg_session_duration | average_session_duration | averageSessionDuration |
| bounceRate | bounce_rate | bounceRate |
| activeUsers | active_users or users | activeUsers |
| key_events, keyEvents, conversions_generate_lead and other conversions_<event> names | conversions, with event_name and a filter on the event | keyEvents |
| conversion_rate | session_conversion_rate or user_conversion_rate | sessionKeyEventRate, userKeyEventRate |
| addToCarts | add_to_carts | addToCarts |
| ecommercePurchases, purchases | ecommerce_purchases (event level) or items_purchased (per item) | ecommercePurchases, itemsPurchased |
| landingPage | landing_page | landingPage |
| pagePath | page_path | pagePath |
| page_title, pageTitle | pagetitle | pageTitle |
| itemName, itemRevenue, itemsPurchased, itemsViewed, itemsAddedToCart | item_name, item_revenue, items_purchased, items_viewed, items_added_to_cart | same names in camelCase |
| item_views | items_viewed (per item) or item_view_events (event level) | itemsViewed, itemViewEvents |
| transaction_id, transactionId | transactionid | transactionId |
| yearMonth | year_month | yearMonth |
| accountname, accountid, property_id, property_name | account_name and account_id exist; the others do not. See the note below | none |
Rule of thumb when a name is not in this table: write Google’s API name in snake_case, then check it against the field reference. If it is still rejected, search the reference for the label you see in the GA4 interface.
Custom dimensions, custom metrics and custom channel groups are exposed, but only in the field list for your own account, and under generated IDs rather than the names you gave them: customevent_<parameter> for event-scoped custom dimensions, customuser_<parameter> for user-scoped ones, countcustomevent_ and averagecustomevent_ variants for custom metrics, and sessioncustomchannelgroup_<id> or customchannelgroup_<id> for custom channel groups. Call get_fields for your account, or open the field list in the Windsor app, to see the exact IDs. Parameter names that contain characters other than letters, numbers and underscores cannot be requested; that is the “GA4 API allows requesting only metrics which include letters, numbers, or _” error.
account_name and account_id on GA4: “Did you mean adSourceName?”
A related error reads “Did you mean adSourceName? Field account_name is not a valid dimension” or “Did you mean country? Field account_id is not a valid dimension”, followed by “This field might not exist for the [property] account”. Here the names exist in Windsor’s list and work on most properties, but on some the request is passed on to Google with the field included, and Google answers with its nearest guess. The suggestion (adSourceName, country) is unrelated to what you wanted, so ignore it.
- Fix: drop account_name and account_id from that request. To limit a request to one property, pass its ID in the accounts parameter (the MCP tool and the API both have it) instead of selecting an account field.
- Ask your assistant: “Drop account_name and account_id from the GA4 field list and restrict the request to property [ID] with the accounts parameter.”
General troubleshooting checklist
- Copy every name inside the braces of the error. All of them must change; fixing one and retrying only surfaces the next.
- Look each one up in the table above, or in the field reference. In Claude or ChatGPT, ask the assistant to call get_fields for googleanalytics4 and pick from that list.
- Watch the five lowercase legacy IDs: totalusers, newusers, totalrevenue, pagetitle, transactionid. Snake_case versions of these do not exist.
- Remember the renamed ones: conversions for key events, source and medium for session source and medium, campaign for session campaign name.
- Do not request per-event key-event metrics (conversions_purchase, conversions_form_submit). Use conversions with event_name and filter on the event.
- Once every name is valid, the request may still fail on scope. That is a different error, covered in the GA4 “Fields cannot be queried together” guide.
Still stuck?
If a field you can see in the GA4 interface has no equivalent in the field reference or in get_fields for your account, reach out to support with:
- The label as shown in GA4 and, if you know it, the API name from the GA4 Dimensions & Metrics Explorer.
- Whether it is a standard field or a custom dimension, metric or channel group you created, and the parameter name behind it.
- The report you are trying to build.
Missing standard fields get added to the connector.
Related reading
Tired of juggling fragmented data? Get started with Windsor.ai today to create a single source of truth