---
title: "One dropdown took one element. The one next to it took four."
description: "Two dropdowns on the same screen flow, identical to the user. One is a record choice set. The other has to reach a Location through AssociatedLocation, which no record choice set can do — and the metadata element for the thing that can does not exist under the name you would guess."
source: "https://noconaconsulting.com/blog/flow-collection-choice-set-junction-object/"
---

# One dropdown took one element. The one next to it took four.

_Build log · Doorvana · Garage door manufacturing & service_

Two dropdowns on the same screen flow, identical to the user. One is a record choice set. The other has to reach a Location through AssociatedLocation, which no record choice set can do — and the metadata element for the thing that can does not exist under the name you would guess.

Nocona Consulting · October 8, 2026 · 7 min read

The phone rings. A homeowner’s garage door will not close. The office has the customer’s household record on screen and needs to book a visit, which means answering two questions: **who called**, and **which of their properties is the door at**.

On the finished screen those are two dropdowns, one under the other, and they look exactly the same. Behind them, one is a single declarative element and the other is four.

## The dropdown that costs one element

“Who called?” is a record choice set. `Contact` has an `AccountId`, the quick action runs from the Account, so the filter writes itself:

force-app/main/default/flows/Book\_Service\_Call.flow-meta.xml

```xml
<dynamicChoiceSets>
    <name>Contact_Choices</name>
    <dataType>String</dataType>
    <displayField>Name</displayField>
    <filterLogic>and</filterLogic>
    <filters>
        <field>AccountId</field>
        <operator>EqualTo</operator>
        <value>
            <elementReference>recordId</elementReference>
        </value>
    </filters>
    <object>Contact</object>
    <sortField>Name</sortField>
    <sortOrder>Asc</sortOrder>
    <valueField>Id</valueField>
</dynamicChoiceSets>
```

One element. It queries when the screen renders, it sorts, it needs no variables. This is what a record choice set is for and it is genuinely good.

## Why the property dropdown cannot work the same way

`Location` has **no Account lookup**. There is no field on `Location` to filter on, because the relationship between a customer and a physical place is not a lookup at all — it is `AssociatedLocation`, the standard junction, where `ParentRecordId` points at the `Account` and `LocationId` at the `Location`.

That separation is deliberate and it is the reason the data model works: a household that moves keeps its record and gains an address, and a property outlives whoever lived in it. It is also the reason no record choice set can produce this list. A record choice set queries **one** object and filters on **that object’s own fields**. There is no traversal, no subquery, no `AssociatedLocation.ParentRecordId` to reach for from `Location`.

The cost of a junction shows up in the UI layer, not the data layer

A junction is almost always the right modelling call, and the bill arrives later and somewhere else: every declarative control that wants to offer “the related records” has to be assembled by hand. Worth knowing when you choose the junction, not when you build the screen.

## Four elements to fill one picklist

1.  **Get Records** on `AssociatedLocation`, `ParentRecordId` equal to the Account’s `recordId`, returning `Id` and `LocationId`.
2.  **Decision** — does this customer have a property at all? An empty collection is the normal case for a brand-new household, and it gets its own screen: _“This customer has no property on file. A service call is booked at a property. Add the property as a Location linked to this account (Associated Locations), then book the call again.”_ A dropdown with nothing in it is not an error message.
3.  **Loop** over the junction records with an **Assignment** that `Add`s each `LocationId` to a text collection variable.
4.  **Get Records** on `Location`, `Id` `In` that collection.

Then the choice set itself, over the result of step four:

force-app/main/default/flows/Book\_Service\_Call.flow-meta.xml

```xml
<dynamicChoiceSets>
    <description>Collection choice set: the household's properties, gathered through
        AssociatedLocation because Location carries no Account lookup.</description>
    <name>Property_Choices</name>
    <collectionReference>Get_Properties</collectionReference>
    <dataType>String</dataType>
    <displayField>Name</displayField>
    <object>Location</object>
    <valueField>Id</valueField>
</dynamicChoiceSets>
```

On the screen, the two are indistinguishable — both are a `DropdownBox` field with a `choiceReferences` pointing at their choice set. The receptionist sees two dropdowns. One of them has a loop behind it.

## The element name that does not exist

Here is the part worth the search result. In Flow Builder these are two visibly different things: **Record Choice Set** and **Collection Choice Set**, separate items in a separate menu. The natural assumption when hand-writing the metadata is that they are two elements.

They are not. Writing `<collectionChoiceSets>` gets this:

```plaintext
Element '{http://soap.sforce.com/2006/04/metadata}collectionChoiceSets' is invalid at this
location in type Flow
```

A collection choice set **is** a `dynamicChoiceSets` entry. The two are told apart entirely by which children they carry:

|  | Record choice set | Collection choice set |
| --- | --- | --- |
| Element | `dynamicChoiceSets` | `dynamicChoiceSets` |
| Source | `object` + `filters` | `object` + `collectionReference` |
| Queries | on screen render | never — the collection is already in memory |
| Sorting | `sortField` / `sortOrder` | whatever the Get Records did |

`collectionReference` takes the name of a collection variable or, as here, a Get Records element with `storeOutputAutomatically`. Once you know it is one element with two modes, the XML is obvious. Until you know, the error tells you nothing about what to write instead — it only says what you wrote is not a thing.

Rule of thumb

When a Metadata API deploy rejects an element name outright, the feature is usually not missing — it is a mode of an element that already exists. Retrieve a version built in the UI and diff it against what you wrote, rather than searching for the name the builder showed you.

## The appointment the Work Type would have created twice

With the property chosen, the flow creates the `WorkOrder` — Work Type `Diagnosis`, Line of Business Service, the active `ServiceTerritory`, the standard price book, address copied from the `Location`’s `Site_*` fields — and then its first `ServiceAppointment`.

Field Service will create that appointment for you. A `WorkType` has an auto-create setting that produces a `ServiceAppointment` when a Work Order is created against it, and it is the right default in most orgs.

It is turned **off** here, because of two fields:

force-app/main/default/flows/Book\_Service\_Call.flow-meta.xml

```xml
<inputAssignments>
    <field>EarliestStartTime</field>
    <value><elementReference>Available_From</elementReference></value>
</inputAssignments>
<inputAssignments>
    <field>DueDate</field>
    <value><elementReference>Due_By</elementReference></value>
</inputAssignments>
<inputAssignments>
    <field>Duration</field>
    <value><elementReference>Get_Diagnosis_Work_Type.EstimatedDuration</elementReference></value>
</inputAssignments>
<inputAssignments>
    <field>DurationType</field>
    <value><elementReference>Get_Diagnosis_Work_Type.DurationType</elementReference></value>
</inputAssignments>
```

`EarliestStartTime` and `DueDate` are the customer’s availability window — the single most valuable thing collected on the call, and the thing the dispatcher schedules against. Auto-create cannot carry it. It produces an appointment with the Work Type’s duration and no window at all, which means the dispatcher has to phone the customer back to ask the question that was already answered.

So the flow creates the appointment, and auto-create stays off. Leaving both on is the obvious failure: two appointments for one visit, on a job model where the number of open appointments is exactly what decides whether the job may be completed.

`Duration` and `DurationType` still come from the Work Type rather than being written into the flow, so a diagnosis slot that turns out to be ninety minutes rather than sixty is a Work Type edit and not a deployment.

StateCode, not State

The appointment’s address is copied from the Location, and the state goes to `StateCode` — because `Site_State__c` is a restricted picklist keyed on codes, holding `TN` rather than `Tennessee`. `State` is the text field and `StateCode` is the picklist. Copying the wrong one into the wrong one is a bug this project has already shipped once, in a flow that also looked obviously correct.

Both record creates carry a fault connector to a screen that shows `$Flow.FaultMessage` and names the two likely causes — no Diagnosis Work Type, no active Service Territory. A screen flow without a fault path on a create hands the receptionist a raw platform fault while a customer is on the phone.

## Two more names that do not resolve

Adding a list view for jobs parked waiting on a part produced a second naming lesson in the same session:

```plaintext
Could not resolve list view column
```

List view columns in the Metadata API want field API names and **relationship** names. The Classic-era tokens — `WORK_ORDER_NUMBER`, `ACCOUNT.NAME`, `SUBJECT`, `STATUS` — are all rejected. So are the raw Id fields, `AccountId`, `LocationId` and `OwnerId`. And so is `WorkOrderNumber`, which is a real field and still not the right column name.

What works: `Name` for the record name, `Account` and `Location` for the lookups, then `Subject`, `Status`, `LastModifiedDate`.

## Practical summary

-   A record choice set queries one object and filters its own fields. It cannot see through a junction, so a choice of junction-linked records needs Get Records, a loop, a collection, a second Get Records, and a collection choice set over the result.
-   Handle the empty collection with a screen that says what to do. It is the normal first-time case, not an error.
-   There is no `collectionChoiceSets` element. A collection choice set is a `dynamicChoiceSets` entry with `collectionReference` in place of `filters`.
-   Turn off Work Type appointment auto-create the moment your process knows something auto-create cannot carry — an availability window, a specific resource, anything. Otherwise you get two appointments for one visit.
-   Keep `Duration` sourced from the Work Type so timing stays configuration.
-   List view columns use API names and relationship names. `Name`, not `WorkOrderNumber`; `Account`, not `AccountId`.

What is not yet proven

All of the above is deployed to the development sandbox and the metadata validates. The flow has not yet been run end to end in the Lightning UI by a person, so the claims here are about what the platform accepts, not yet about what the office experiences. Those are separate claims and this project has learned to keep them apart.

-   Flow
-   Field Service
-   AssociatedLocation
-   Metadata API
-   Screen Flow
-   Work Order

Disclosure: Doorvana's owner is also a co-owner of Nocona Consulting. This is a real engagement between two separate companies, but not an arm's-length one — and not an independent reference. [More on the work page](https://noconaconsulting.com/work/).

## FAQ

### Can a Salesforce Flow record choice set filter through a junction object?

No. A record choice set queries exactly one object and filters on that object's own fields, so it cannot reach a related record through a junction. To offer a choice of records linked by a junction, query the junction with Get Records, collect the target Ids into a collection variable, query the target object with an Id IN collection filter, and build a collection choice set over that result. That is four elements where a record choice set would have been one.

### What is the Metadata API element name for a Flow collection choice set?

It is dynamicChoiceSets, the same element as a record choice set. The two are distinguished by their children: a record choice set carries object plus filters, while a collection choice set carries object plus collectionReference pointing at a collection variable or a Get Records result. There is no collectionChoiceSets element — writing one is rejected at deploy with an error that the element is invalid at this location in type Flow.

### Why does a Location not appear in a choice of an Account's properties in Salesforce?

Because the standard Location object has no Account lookup. The relationship between an Account and a physical place is the AssociatedLocation junction, where ParentRecordId points at the Account and LocationId at the Location. Any list of an account's properties has to be assembled through that junction; filtering Location directly on an account field finds nothing, because no such field exists.

### Should a Work Type auto-create the Service Appointment, or should a flow create it?

It depends on whether anything about the appointment is known at creation time that auto-create cannot carry. Auto-create produces a Service Appointment with the Work Type's duration and nothing else, so if the process captures a customer availability window that belongs on EarliestStartTime and DueDate, the flow has to create the appointment and auto-create must be turned off. Leaving both on produces two appointments for one visit.

### Why does a Salesforce list view column fail with Could not resolve list view column?

Because list view columns in the Metadata API use field API names and relationship names, not the Classic-era column tokens. Name rather than WorkOrderNumber for the record name, and the relationship name rather than the Id field for a lookup — Account, not AccountId. The Classic-style uppercase tokens such as WORK\_ORDER\_NUMBER and ACCOUNT.NAME are rejected, and so are the raw Id fields.
