Try Serpent Free

How to fix CANNOT_UPDATE_CONVERTED_LEAD in Salesforce deployments

Apex or API code tries to update a Lead after it's already been converted, and Salesforce blocks the write.

Surfaces during: runtime DML, most often inside a trigger, batch job, or Apex test

What it means

CANNOT_UPDATE_CONVERTED_LEAD fires when Apex or an API call attempts to update a Lead record that has already gone through conversion. Once converted, a Lead becomes read-only outside a small set of fields Salesforce still allows, so any other update is rejected rather than silently ignored.

It's a runtime error rather than a deploy-time one: the trigger or batch job itself deploys cleanly, and the failure only appears once the code actually executes against a converted record, commonly inside the Apex tests a production deployment requires.

Diagnosis

Common causes

Trigger or batch job processes Leads without checking IsConverted
Automation updates Lead records in bulk without filtering out ones that have already been converted.
Migration script updates Leads without excluding converted ones
A data migration touches the Lead object broadly and doesn't scope its update to unconverted records only.
Race condition between query and DML
A Lead gets converted between when Apex queries it and when the same transaction attempts to update it.

The fix

  1. Filter out IsConverted = true records before updating Leads
    Add IsConverted = false to any query or WHERE clause driving a bulk Lead update.
    List<Lead> leads = [
        SELECT Id, Status FROM Lead
        WHERE IsConverted = false AND Id IN :leadIds
    ];
    update leads;
  2. Add IsConverted checks to trigger logic
    Guard Lead trigger handlers so converted records are skipped rather than passed into update logic.
  3. Update the resulting record instead
    Once a Lead is converted, update the Contact, Account, or Opportunity it created rather than the original Lead.
In practice

How Serpent prevents this

Because Serpent runs the full Apex test suite on every task before it's eligible to merge, a trigger that doesn't filter out converted Leads fails as a test failure on the branch that introduced it, not on a production release. See the Salesforce deployment error library.

Metadata and data in one deployment flow in Serpent

Prevention

Make IsConverted filtering a standard trigger-handler guard
Add the IsConverted check to a shared trigger helper or handler base class so every Lead automation inherits it, instead of relying on each developer to remember.
Cover a converted Lead in every Lead trigger's test class
Add a test method that converts a Lead mid-test and confirms the trigger skips it cleanly, so a missing filter fails CI instead of production.
Scope bulk Lead jobs narrowly
Give batch jobs and migration scripts an explicit, narrow WHERE clause rather than processing the entire Lead object.
Common questions

CANNOT_UPDATE_CONVERTED_LEAD, answered

Can I update any field at all on a converted Lead?
A small set of fields, like ConvertedContactId-related fields Salesforce manages itself, but ordinary business fields on a converted Lead are locked.
Does this apply to Lead deletes too, or only updates?
Deletes are handled separately and are generally allowed on converted Leads if the org's settings permit deleting Leads at all; this specific error is about update-style DML.
Why does the same trigger pass in a scratch org but fail in a full sandbox?
A scratch org's test data is unlikely to include converted Leads unless the test explicitly creates one. A full sandbox copy of production almost certainly has converted Leads the bulk job now touches.

Start free. No credit card, no install, no commitment.

Set up in under 15 minutes. No DevOps hire needed.

Curious about faster shipping before you dive in? Let's talk

Commitment free!