STRING_TOO_LONG: data value too large
You tried to write a value to a field that exceeds the field's length limit. The error names the field. Either truncate before insert, increase the field length, or move to a long-text field.
Also seen asSTRING_TOO_LONG·data value too large·STRING_TOO_LONG: data value too large
A data-migration job that worked in the sandbox blows up the second time it runs in production. The log: STRING_TOO_LONG: data value too large: <some-long-text> (max length=255). One value in the import has 312 characters. The source system's notes field is unbounded; the destination Salesforce field caps at 255. The job worked the first time only because that record happened to have a short value. The second time, a different record had a long one.
What the error is telling you
Every text-type field in Salesforce has a maximum length defined in its metadata. The platform enforces the length on every save. If your code (or a user, or an integration) tries to write a value longer than the field allows, the platform throws STRING_TOO_LONG: data value too large. The message includes the offending field name and (in some versions) the max length.
The check applies to:
- Text fields (default 255 max, configurable up to 255).
- Text Area (Long) fields (configurable up to 131,072 characters).
- Rich Text Area fields (configurable up to 131,072 characters).
- Picklist fields (the value must match an existing option, but a too-long custom value also produces a similar error).
- Phone, Email, URL, External Id (standard typed fields with their own length caps).
The error fires server-side. Apex code can pre-check, but the authoritative validation is the platform's save handler.
The broken example
A data-import script that copies legacy notes into a Description field:
public class LegacyNoteImporter {
public static void importNotes(List<Legacy_Note__c> legacy) {
List<Account> accounts = new List<Account>();
Map<Id, String> notesByAccount = new Map<Id, String>();
for (Legacy_Note__c n : legacy) {
String existing = notesByAccount.get(n.Account__c) ?? '';
notesByAccount.put(n.Account__c, existing + '\n' + n.Body__c);
}
for (Id accountId : notesByAccount.keySet()) {
accounts.add(new Account(
Id = accountId,
Description = notesByAccount.get(accountId)
));
}
update accounts;
}
}
Account.Description is a Text Area field with a 32,000-character default. The script concatenates legacy notes into the Description. If an account has many legacy notes, the concatenated value exceeds the cap, and the entire batch update fails.
The fix: validate length before save
Apex provides DescribeFieldResult.getLength() which returns the max length of a field at runtime. Use it to truncate or split values before saving.
public static void importNotes(List<Legacy_Note__c> legacy) {
Integer descriptionMax = Account.Description.getDescribe().getLength();
Map<Id, String> notesByAccount = new Map<Id, String>();
for (Legacy_Note__c n : legacy) {
String existing = notesByAccount.get(n.Account__c) ?? '';
notesByAccount.put(n.Account__c, existing + '\n' + n.Body__c);
}
List<Account> accounts = new List<Account>();
for (Id accountId : notesByAccount.keySet()) {
String combined = notesByAccount.get(accountId);
if (combined.length() > descriptionMax) {
combined = combined.left(descriptionMax - 100)
+ '\n\n[Truncated; original was ' + combined.length() + ' chars.]';
}
accounts.add(new Account(
Id = accountId,
Description = combined
));
}
update accounts;
}
The fix has three parts:
- Discover the field length dynamically. Hardcoding
255or32000works but fails the first time the field's length changes. The describe call always reflects current metadata. - Truncate with a margin. Leave 100 characters for a "Truncated" suffix so the data signals it was clipped.
- Document the truncation. A reader of the truncated record knows that the original was longer.
The fixed example, end to end
A production-shaped import service:
public class LegacyNoteImporter {
public class ImportResult {
public Integer processed;
public Integer truncated;
public List<Id> failedIds;
}
public static ImportResult importNotes(List<Legacy_Note__c> legacy) {
ImportResult r = new ImportResult();
r.failedIds = new List<Id>();
Integer descMax = Account.Description.getDescribe().getLength();
Map<Id, String> notesByAccount = new Map<Id, String>();
for (Legacy_Note__c n : legacy) {
String existing = notesByAccount.get(n.Account__c) ?? '';
notesByAccount.put(n.Account__c, existing + '\n' + n.Body__c);
}
r.processed = notesByAccount.size();
List<Account> updates = new List<Account>();
for (Id accountId : notesByAccount.keySet()) {
String body = notesByAccount.get(accountId);
if (body.length() > descMax) {
r.truncated++;
body = body.left(descMax - 100)
+ '\n\n[Truncated; original was ' + body.length() + ' chars.]';
}
updates.add(new Account(Id = accountId, Description = body));
}
Database.SaveResult[] results = Database.update(updates, false);
for (Integer i = 0; i < results.size(); i++) {
if (!results[i].isSuccess()) {
r.failedIds.add(updates[i].Id);
}
}
return r;
}
}
The handler reports how many records were processed, how many had truncation applied, and which (if any) still failed. Surfacing these counts is essential for a maintainable bulk import.
When the field is too short, not the data
Sometimes the right fix isn't on the data side. The field length is too restrictive for legitimate business values, and increasing it produces fewer downstream issues than truncating.
To increase a field's length:
- Setup → Object Manager → object → Fields & Relationships.
- Click the field name.
- Edit and increase the Length value.
- Save.
For text fields, the maximum is 255. Going beyond 255 requires switching to Text Area (Long) which has a different storage representation. The switch isn't reversible (you can't go from Text Area back to Text), and it changes which features (formula references, indexability, reportability) the field supports.
Consult the field's downstream usage before increasing. A field that participates in a unique constraint or an external id can't be lengthened freely.
Text vs Long Text Area vs Rich Text
Salesforce offers three text field types, each with different cap:
| Type | Default Max | Storage | Searchable | Formula-Referenceable |
|---|---|---|---|---|
| Text | 255 | Indexed | Yes | Yes |
| Text Area | 255 | Indexed | Yes | Yes |
| Text Area (Long) | 131,072 | Not indexed | Limited | No |
| Text Area (Rich) | 131,072 | Not indexed | Limited | No |
The choice depends on intended use. For short user-entered notes (names, codes, labels), Text or Text Area is correct. For multi-paragraph content (descriptions, transcripts), Long or Rich Text Area is the right home.
Switching types changes feature availability. Plan carefully and test downstream code after the switch.
A common gotcha with field-level merge
Workflows, processes, and flows that "merge" a value into a field can produce STRING_TOO_LONG when the merged result exceeds the target's cap. For example, a Process Builder action that appends a status message to a Description field eventually overflows the field after enough updates.
The fix is structural: don't accumulate unbounded data into a bounded field. Either:
- Use a long text area for the destination.
- Replace, not append, on each update.
- Move the running log to a separate object (
Account_Note__cwith one row per note).
Each option has trade-offs. The third is cleanest for high-volume accumulation; the second is simplest if the data is genuinely a current-status field.
Apex test that catches the regression
@isTest
static void importNotes_truncatesWhenOverLimit() {
Account a = new Account(Name = 'Test Account');
insert a;
Integer maxLen = Account.Description.getDescribe().getLength();
String longBody = 'X'.repeat(maxLen + 500);
Legacy_Note__c n = new Legacy_Note__c(Account__c = a.Id, Body__c = longBody);
insert n;
Test.startTest();
LegacyNoteImporter.ImportResult result = LegacyNoteImporter.importNotes(new List<Legacy_Note__c>{ n });
Test.stopTest();
System.assertEquals(1, result.truncated, 'One record should have been truncated');
System.assertEquals(0, result.failedIds.size(), 'No failures should occur');
Account updated = [SELECT Description FROM Account WHERE Id = :a.Id];
System.assertNotEquals(null, updated.Description);
System.assert(updated.Description.length() <= maxLen, 'Truncated to within max length');
System.assert(updated.Description.contains('[Truncated'), 'Should annotate truncation');
}
The test seeds an oversized note and verifies the import truncates correctly. The assertion uses the live getLength() value, so the test stays correct if the field length changes in metadata.
Picklist values and field-length analogues
A picklist field has its own length cap on each value (255 by default) and a cap on the total number of values (1,000 for standard picklists, smaller for multi-select). Trying to insert a record whose picklist value isn't in the defined set produces INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST, not STRING_TOO_LONG. But trying to add a new picklist option that's too long produces a related error during metadata deploy.
The diagnostic move is to check both the field's length and the picklist's defined values whenever a save fails on a picklist field.
Multi-byte character considerations
The field's length cap is measured in characters, not bytes. A 255-character Text field can hold 255 Unicode characters, each of which may be multiple bytes in UTF-8. This is rarely a problem in practice (Salesforce normalizes consistently), but if you're processing input from systems that count bytes (older legacy databases, some C++ APIs), the character/byte distinction can produce surprise truncations.
If you must preserve byte-equivalent semantics, store a byte-count alongside the field and validate explicitly.
When the error message names a field you didn't expect
The platform names the violating field in the error message, but sometimes the field is one you didn't touch directly. Three common cases:
Workflow field updates. A workflow rule on the saved record updates a different field, and that update produces an oversized value. The save fails on the workflow's update, not your original write.
Process Builder or Flow side-effects. A record-triggered flow runs Update Records on the same record, populating a field with a derived value that exceeds its cap.
Validation rule with formula updates. Rarely, a validation rule's formula triggers a downstream side-effect that overflows.
The diagnostic: trace the automation chain on the object. Setup → Object Manager → object → Flows and Process Builder and Workflow Rules. Look for any automation that updates a text field. Inspect each updater's source value for unbounded length.
A common architectural pattern: separate object for append-only data
Apps that accumulate notes, comments, or activity logs run into STRING_TOO_LONG eventually if the data lives in a bounded field. The clean solution is to model the data as a child object with one row per entry:
Account
├── Account_Note__c (one record per note)
│ ├── Body__c (Long Text Area)
│ ├── Created_Date__c
│ └── Created_By__c
Now appending a new note inserts a new row instead of growing a single field. The data scales without bumping into field caps. Queries against the child object can return all notes, recent notes, or filtered notes as needed.
The trade-off is more queries (one for the parent, one for the children) and more storage records. For high-volume teams, the trade-off is almost always worth it.
Using getLength() defensively in every save
A discipline that prevents most STRING_TOO_LONG incidents: before any save that includes a text field whose source could be unbounded, check the length. A small helper class makes this consistent:
public class FieldLengthGuard {
public static String fitField(Schema.SObjectField field, String value) {
if (value == null) return null;
Integer max = field.getDescribe().getLength();
if (value.length() <= max) return value;
return value.left(max - 50) + '... [truncated]';
}
}
Use it everywhere:
account.Description = FieldLengthGuard.fitField(Account.Description, computedValue);
The two extra lines per save are cheap. The bug class disappears.
Integration patterns that respect the cap
For systems that pull data from Salesforce and push it back transformed, design the integration to know the field cap. Two patterns work:
Pre-fetch metadata. At integration setup time, fetch the field's metadata once and cache the max length. The integration's transform layer truncates to that length before pushing.
Read-modify-write with length check. When updating a field, first read the current value's length, factor in the change you're about to make, and skip the write if the result would exceed the cap.
For high-traffic integrations that update text fields many times per minute, also include STRING_TOO_LONG as a retryable-with-truncation error in the integration's error handler. Don't silently swallow it; log, truncate, and retry.
Further reading from Salesforce
Related dictionary terms
Share this fix
Related Validation errors
DUPLICATE_VALUE: duplicate value found
ValidationYou tried to insert or update a record with a value on a unique field (External ID set to "Unique" or one of the unique standard fields like…
ENTITY_IS_DELETED: entity is deleted
ValidationYou tried to update or query a record that's in the recycle bin (soft-deleted). Either undelete it first, query with `ALL ROWS` to include d…
ENTITY_IS_LOCKED: entity is locked for approval
ValidationThe record is currently in an Approval Process and is locked for editing until the approval is granted, rejected, or recalled. The lock is e…
FIELD_CUSTOM_VALIDATION_EXCEPTION: <your validation rule's error message>
ValidationA validation rule on the object evaluated to TRUE for the record being saved, blocking the save. The text after the colon is the rule's own …
INACTIVE_OWNER_OR_USER: operation performed with inactive User
ValidationYou set a record's Owner (or another User reference) to a deactivated user. Salesforce blocks new ownership pointing at inactive users by de…