---
title: "Two fields both called state, and only one of them will take TN"
description: "With State and Country/Territory Picklists on, Lead.State holds Tennessee and Lead.StateCode holds TN. A Flow copying the wrong one into a restricted picklist looked correct, deployed clean, and would have failed every enquiry with an address on it."
source: "https://noconaconsulting.com/blog/lead-state-vs-statecode-restricted-picklist/"
---

# Two fields both called state, and only one of them will take TN

_Build log · Doorvana · Garage door manufacturing & service_

With State and Country/Territory Picklists on, Lead.State holds Tennessee and Lead.StateCode holds TN. A Flow copying the wrong one into a restricted picklist looked correct, deployed clean, and would have failed every enquiry with an address on it.

Nocona Consulting · October 2, 2026 · 6 min read

A homeowner calls about a broken spring, gives an address, and the enquiry gets filed. Behind that, automation is supposed to create the property record the technician will eventually be dispatched to. It would have failed. Not on some enquiries — on every single one that had an address on it, which is all of them.

The cause was one line that copied a field called _state_ into another field called _state_.

## Two fields, one name, different contents

Doorvana’s org has **State and Country/Territory Picklists** enabled. That is the right call for anything that will be reported on or routed by geography, and it is close to mandatory before you start matching addresses across objects. What it does not do is replace the address fields you already had. It adds new ones next to them.

| Field | Type | Holds |
| --- | --- | --- |
| `Lead.State` | Text | `Tennessee` |
| `Lead.StateCode` | Picklist | `TN` |
| `Lead.Country` | Text | `United States` |
| `Lead.CountryCode` | Picklist | `US` |

Both are populated. Both are visible. On the record page they render as one address block, so a user sees a single state, picks it once, and has no idea two fields just got written. The same pairing exists on every standard address on the platform — `BillingState` / `BillingStateCode`, `ShippingState` / `ShippingStateCode`, `MailingState` / `MailingStateCode`.

## Where the property record comes from

Under this org’s model, a service address is not a field on the Account. It is a standard `Location` record, joined to the household by `AssociatedLocation`, for the reasons set out in [the household account write-up](https://noconaconsulting.com/blog/household-accounts-without-person-accounts/). Standard lead conversion does not create that Location, so a Flow does.

The Location carries plain address fields rather than a `VisitorAddressId` lookup — [benchmarked, not argued about](https://noconaconsulting.com/blog/job-site-address-circular-delete/) — and one of them is `Site_State__c`. To stop `TX`, `Texas` and `tx` from being three different states in every report, `Site_State__c` is a **restricted picklist on a `US_State` global value set**, and that value set is keyed on the two-letter code.

So the destination accepts `TN`. It accepts nothing else that means Tennessee.

## The line that was obviously correct

force-app/main/default/flows/Create\_Location\_From\_Lead.flow-meta.xml

```xml
<!-- before -->
<recordCreates>
    <name>Create_Site_Location</name>
    <inputAssignments>
        <field>Site_State__c</field>
        <value><elementReference>$Record.State</elementReference></value>
    </inputAssignments>
</recordCreates>
```

Read that as a sentence. _Put the lead’s state on the location’s state field._ It is what you would write, it is what a reviewer reads, and it is wrong. `$Record.State` resolves to `Tennessee`. `Site_State__c` will only take `TN`.

FIELD\_INTEGRITY\_EXCEPTION on a value that is obviously a state

A restricted picklist compares against its stored values, not against meaning. Assigning `Tennessee` to a picklist whose value set contains `TN` is rejected with _“bad value for restricted picklist field”_ and a `FIELD_INTEGRITY_EXCEPTION`. The error names the value it refused, which reads like nonsense — the value is a real state, spelled correctly — until you remember which of the two fields you read from.

The fix is four characters.

force-app/main/default/flows/Create\_Location\_From\_Lead.flow-meta.xml

```xml
<!-- after -->
<inputAssignments>
    <field>Site_State__c</field>
    <value><elementReference>$Record.StateCode</elementReference></value>
</inputAssignments>
```

_The user picks a state once. The platform writes both the label field and the code field. A restricted picklist built on a code-keyed global value set accepts the right-hand path and rejects the left-hand one, even though a reader of the Flow cannot tell the two paths apart._

## What it would have cost

Worth being precise about the blast radius, because it is larger than it sounds.

The Flow creates the Location. The Location is what an Opportunity points at, what an `AssociatedLocation` junction hangs off, and ultimately what a Work Order is scheduled against. A failed Location create does not produce a lead with a slightly wrong state. It produces a converted lead with **no property record at all**, and a Field Service chain with nothing to dispatch to.

Every enquiry carrying an address is every enquiry. There is no subset here that still works.

## How it was caught

One fabricated enquiry, followed all the way through.

Not a unit test of the Flow. Not a review of the metadata. One invented record — a homeowner, a street address, a request type — created at the top of the funnel and then followed through every automation it triggered until it either produced the whole downstream chain or stopped. It stopped.

Rule of thumb

Unit-test the steps if you like, but the thing that finds mapping errors is one fake record walked end to end. A wrong mapping is syntactically perfect and semantically plausible, which is exactly the class of bug that review cannot see and traversal cannot miss.

The same test found the same mistake in a second place that nobody had exercised yet. Both were fixed in the same pass, which is the part that makes end-to-end testing pay: the bug is rarely alone, because whoever wrote it wrote it more than once.

## Why review could never have caught it

This is the honest version of the lesson, and it is not “be more careful.”

Both fields are called state. Both contain a state. They sit side by side on the same screen and differ by four characters in the API name. The assignment reads as a correct English sentence. Nothing in Flow Builder warns you that the destination is restricted, nothing tells you the source is the label rather than the code, and the metadata deploys clean because nothing about it is invalid — it is only wrong at runtime, with real data, in one direction.

The tooling has all the information required to flag this and does not.

## What to actually do about it

Three things, in order of how much they are worth.

**Map from the Code fields whenever the destination is code-keyed.** `StateCode` and `CountryCode`, not `State` and `Country`. If you have enabled State and Country/Territory Picklists, treat the text fields as display artefacts and stop reading them in automation.

**Make the destination restricted on purpose.** A restricted picklist on a global value set is what turned this from a silent data-quality rot into a loud runtime failure. An unrestricted picklist would have accepted `Tennessee`, stored it happily, and left a property base where some rows say `TN` and some say `Tennessee` and no report over geography is trustworthy again. The error is the feature.

**Grep the org for the other one.** If a mapping like this exists once, assume it exists twice. Searching the retrieved source for `elementReference>$Record.State<` takes a second and answers the question properly, which reading Flows in the UI does not.

The whole fix was four characters. Finding it was one fake customer and twenty minutes.

-   Flow
-   Picklists
-   Global value sets
-   Location
-   Lead conversion
-   Data model

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

### What is the difference between Lead.State and Lead.StateCode in Salesforce?

With State and Country/Territory Picklists enabled, they are two separate fields holding two different values. Lead.State is a text field holding the full name, such as Tennessee, and it is what the standard address compound field renders as a label. Lead.StateCode is the picklist holding the ISO-style abbreviation, such as TN, and it is the field that actually carries the selected value. The same pairing exists as BillingState/BillingStateCode, ShippingState/ShippingStateCode and MailingState/MailingStateCode.

### Why does a Flow fail with bad value for restricted picklist field when the value looks correct?

Because a restricted picklist compares against its stored value, not against anything a human would call the same state. If the target picklist is built on a global value set keyed on two-letter codes, then Tennessee is not a member of that value set and TN is, even though both describe the same state. The save is rejected with FIELD\_INTEGRITY\_EXCEPTION and a bad value for restricted picklist field message naming the value that was offered.

### Which field should a Flow read when populating a code-based picklist from a Lead address?

Read the Code field. If the target is a restricted picklist on a global value set of two-letter abbreviations, map from Lead.StateCode and Lead.CountryCode, not from Lead.State and Lead.Country. The label fields exist for display and for orgs that have never enabled State and Country/Territory Picklists, and copying them into a code-keyed picklist produces a value the target will always reject.

### Does enabling State and Country/Territory Picklists change existing Salesforce automation?

It can, because it adds the Code fields alongside the existing text fields rather than replacing them. Automation written before the change continues to read the text field and keeps working wherever the destination is also free text. It starts failing the moment the destination is a restricted picklist keyed on codes, which is why the failure usually appears in new work rather than in the automation that was actually wrong.

### What is the fastest way to catch a field mapping error like this?

One end-to-end test with one fabricated record, followed all the way through every automation it triggers, rather than unit-testing each step in isolation. A mapping that copies a plausible-looking value into the wrong destination passes every review, because reviewers read it as the sentence it appears to be. It only fails when a record actually travels the whole path.
