Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Deployment

INVALID_TYPE: sObject type 'X' is not supported

Your deployment package references an SObject the target org doesn't have. Either it's a custom object that hasn't been deployed yet, a standard object only included with certain editions, or a feature-gated object the org hasn't enabled.

Also seen asINVALID_TYPE·Cannot find sobject·sObject type is not supported·INVALID_TYPE: sObject type

You're deploying a metadata bundle from a sandbox to production. The Apex class compiles in the sandbox, the test suite passes, the change set looks complete. The production deploy fails on a single class with INVALID_TYPE: sObject type 'Renewal_Schedule__c' is not supported. The class exists, the field exists in your sandbox, but production refuses to recognize the object.

What the platform is actually checking

The error means the running context cannot resolve a reference to an SObject type by name. The compiler or the SOQL parser looked up the type in the org's metadata, didn't find it, and bailed. The reasons fall into a few buckets: the object doesn't exist in the target org, the running user lacks permission to see the object, the object's API name was changed, or the object lives in a managed package whose namespace isn't included in the reference.

The check is strict. Salesforce will not compile code that references an unknown type. There is no late binding, no "well, maybe it exists at runtime" fallback. If the metadata is missing at parse time, the deploy fails.

This same error shape appears in SOQL queries (SELECT Id FROM Renewal_Schedule__c), in Apex declarations (Renewal_Schedule__c r = ...), in dynamic SOQL (Database.query('SELECT ... FROM Renewal_Schedule__c')), in describe calls (Schema.SObjectType.Renewal_Schedule__c.getDescribe()), and in Process Builder and Flow references. Every place that names an SObject is a potential failure site.

The broken example

A class that references a custom object that doesn't exist in the target org:

public class RenewalProcessor {
    public static void process() {
        List<Renewal_Schedule__c> schedules = [
            SELECT Id, Account__c, Renewal_Date__c
            FROM Renewal_Schedule__c
            WHERE Renewal_Date__c <= NEXT_N_DAYS:30
        ];
        for (Renewal_Schedule__c s : schedules) {
            s.Status__c = 'In Review';
        }
        update schedules;
    }
}

The class compiles fine in the sandbox where Renewal_Schedule__c was created. The class is added to a change set. The change set ships to production. The deploy fails because the change set didn't include the custom object itself.

A second shape: a class referencing a managed-package object without the namespace:

public class RenewalProcessor {
    public static void process() {
        List<Renewal_Schedule__c> schedules = [SELECT Id FROM Renewal_Schedule__c LIMIT 10];
    }
}

If Renewal_Schedule__c belongs to a managed package with namespace myapp, the actual API name is myapp__Renewal_Schedule__c. The unprefixed reference fails in the subscriber org with INVALID_TYPE. The class works only inside the package itself.

A third shape: a renamed object:

public class RenewalProcessor {
    public static void process() {
        // The object used to be Renewal__c but was renamed to Renewal_Schedule__c.
        List<Renewal__c> stale = [SELECT Id FROM Renewal__c];
    }
}

The reference uses the old API name. Renaming an object's API name in Salesforce is rare but possible during early development. Old code paths that weren't updated fail.

Three paths to a fix

The three causes ranked by frequency in real deploys:

Include the object in the deployment. Open your change set or your package.xml and confirm the custom object is listed under CustomObject. If it isn't, add it. The order of deployment matters: the object must exist in the target before the class that references it can be deployed. For complex deploys with many dependent objects, deploy the data model first as a separate step, then the code.

Include the namespace prefix for managed-package objects. If you're writing code in a subscriber org that needs to reference objects from an installed managed package, prefix the API name with the package's namespace plus two underscores: npe01__Contacts_And_Orgs_Settings__c. The namespace is visible in Setup, then Installed Packages.

Update references to renamed objects. Search the codebase for the old API name, replace with the new one, deploy. A grep pattern like Renewal__c across the repo finds all the references. Update them in sync.

The fixed example

A deployable version that handles all three concerns:

public class RenewalProcessor {
    public static void process() {
        // Verify the object is accessible before query.
        if (!Schema.sObjectType.Renewal_Schedule__c.isAccessible()) {
            throw new SecurityException(
                'User lacks read access to Renewal_Schedule__c'
            );
        }

        List<Renewal_Schedule__c> schedules = [
            SELECT Id, Account__c, Renewal_Date__c
            FROM Renewal_Schedule__c
            WHERE Renewal_Date__c <= NEXT_N_DAYS:30
            WITH SECURITY_ENFORCED
        ];

        List<Renewal_Schedule__c> toUpdate = new List<Renewal_Schedule__c>();
        for (Renewal_Schedule__c s : schedules) {
            toUpdate.add(new Renewal_Schedule__c(
                Id = s.Id,
                Status__c = 'In Review'
            ));
        }
        if (!toUpdate.isEmpty()) {
            update toUpdate;
        }
    }
}

For this class to deploy, the change set or package.xml must include:

<types>
    <members>Renewal_Schedule__c</members>
    <members>Renewal_Schedule__c.Account__c</members>
    <members>Renewal_Schedule__c.Renewal_Date__c</members>
    <members>Renewal_Schedule__c.Status__c</members>
    <name>CustomObject</name>
</types>
<types>
    <members>RenewalProcessor</members>
    <name>ApexClass</name>
</types>

Both the data model and the code ship together. The deploy succeeds.

The metadata dependency story

Salesforce metadata has a dependency graph. An Apex class depends on the objects and fields it references. A Lightning Web Component depends on its targets. A Flow depends on the objects and fields it touches.

The platform doesn't deploy in dependency order automatically (well, it tries, but the change set tool isn't perfect for complex graphs). The author has to think about the order.

For most teams, the practical pattern is:

  1. Custom objects and fields go first.
  2. Validation rules, picklist values, record types follow.
  3. Apex code (classes, triggers, test classes) ships next.
  4. Lightning components, Visualforce pages, and other UI metadata follow.
  5. Workflow rules, processes, flows ship last (these reference everything else).

A change set that mixes layers can fail because Layer 3 references metadata that Layer 1 introduces, and the platform tries to validate the code before the object exists.

The SFDX source format makes this easier because all metadata is text-based and source-controlled, but the dependency order still matters for the deploy.

When the object exists but the field doesn't

A close cousin error: the object compiles, the SOQL references a field that doesn't exist:

INVALID_FIELD: No such column 'Renewal_Date__c' on entity 'Renewal_Schedule__c'

Same family of cause: the field wasn't deployed. The field's metadata change wasn't included in the package. The fix is to include the field metadata alongside the code.

Both errors share a triage path: dump the package.xml, list everything, grep for the missing item, add it, redeploy.

Permission set and profile bundling

If a class references an object that exists in the target org but the running user lacks access, the same INVALID_TYPE error can fire at runtime (not deploy time). The deploy succeeds; the user's first call to the method fails.

The fix is to ensure the user's profile or assigned permission sets grant access to the object:

  1. Setup, then Permission Sets, find or create one named after the feature.
  2. Add Object Permissions for Renewal_Schedule__c (Read, Create, Edit, etc).
  3. Add Field-Level Security for each field the code touches.
  4. Assign the permission set to the user(s) running the code.

For Apex tests, the test method runs as the test user unless runAs is used. Tests can pass in CI even when the actual user can't run the production code, so don't rely on tests to catch permission issues.

Dynamic SOQL and graceful failure

Dynamic SOQL can be defensive in a way that static SOQL can't. Check whether the object exists before referencing it:

public static List<SObject> queryIfPresent(String objectName, String soql) {
    if (!Schema.getGlobalDescribe().containsKey(objectName)) {
        System.debug('Object ' + objectName + ' not present; skipping query.');
        return new List<SObject>();
    }
    return Database.query(soql);
}

This is useful when code needs to work across orgs with different installed packages. Be careful: dynamic SOQL is harder to audit for security and harder for tools like the Apex Code Analyzer to verify.

For most internal code, prefer static SOQL plus explicit deployment of all referenced metadata. Dynamic SOQL is an escape hatch for legitimately variable schemas.

The package versioning angle

Managed packages have versions. When a package is upgraded, an object's API name might change (rare, but possible). Subscriber-org code that references the old API name can fail after the package upgrades.

Mitigation:

Pin to a specific package version in your package.xml rather than upgrading blindly.

Test installs of new package versions in a sandbox before pushing to production.

For first-party managed packages your team owns, treat API-name changes as breaking changes and communicate them.

Test patterns

Deploy validation is the first test. Run sf project deploy validate -d force-app --target-org production before the actual deploy. The validation pass surfaces every INVALID_TYPE and INVALID_FIELD error without actually applying changes.

For Apex tests that exercise object references, an integration test that asserts the describe call succeeds catches missing metadata:

@isTest
static void renewalProcessor_objectExists() {
    Map<String, Schema.SObjectType> all = Schema.getGlobalDescribe();
    System.assert(all.containsKey('Renewal_Schedule__c'),
        'Renewal_Schedule__c must be present in this org');
    Schema.DescribeSObjectResult desc = all.get('Renewal_Schedule__c').getDescribe();
    System.assert(desc.isAccessible(), 'User must have read access');
}

This catches the deployment-completeness question explicitly in the test suite.

A class of error related to record types

Record types live on objects and have their own developer names. Code that references record types by developer name can fail in target orgs that don't have the record type configured:

Id recordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName()
    .get('Strategic_Account')
    .getRecordTypeId();

If the developer name Strategic_Account doesn't exist on Account in the target org, the .get(...) returns null and the subsequent .getRecordTypeId() throws an NPE. Defensively:

Map<String, Schema.RecordTypeInfo> infos = 
    Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName();
Schema.RecordTypeInfo info = infos.get('Strategic_Account');
if (info == null) {
    throw new SecurityException('Strategic_Account record type not configured.');
}
Id rtId = info.getRecordTypeId();

Treat record types like objects: deploy them as metadata, audit their presence, fail loudly when absent.

The legacy package.xml issue

Older deployments use package.xml listings. For a custom object, you must list both the object itself and each field separately if you want to deploy just specific fields:

<types>
    <members>Renewal_Schedule__c</members>
    <name>CustomObject</name>
</types>
<types>
    <members>Renewal_Schedule__c.Renewal_Date__c</members>
    <name>CustomField</name>
</types>

A common bug: listing the object but forgetting to list every field. The deploy succeeds on the object, but a field the code references is missing, and the next compile fails on INVALID_FIELD. Use a metadata-retrieve tool to generate a complete package.xml rather than hand-editing.

When the error says the object exists but type is unsupported

A specific variant: INVALID_TYPE: <type> is not supported in this context. This is different from "not found". The object exists, but you're trying to use it where it isn't allowed.

Examples include:

  • Using Setup objects (like Profile or User) in Database.QueryLocator for Batch Apex. Some objects aren't supported by the QueryLocator.
  • Querying a BigObject with SOQL syntax that BigObjects don't support.
  • Using an external object (from Salesforce Connect) in contexts that require local objects.

Read the full error message for the qualifier. The remediation differs by context.

Habits that prevent this

Three habits eliminate most INVALID_TYPE deploy failures:

Always run sf project deploy validate before sf project deploy start on production deploys. The validation surfaces missing metadata without applying changes, so you find the problem in a 15-minute validation run rather than a partially-applied production deploy that needs rollback. Run the validation against a freshly-refreshed sandbox that mirrors production metadata closely; differences between your dev sandbox and production are themselves a class of bug that validation catches.

Maintain a deploy checklist that includes "objects and fields ship before code references them." A change set or package.xml audit step in PR review catches missing dependencies. The same checklist should call out permission sets, record types, and any other metadata that downstream code might reference implicitly.

When refactoring an object's API name, do a full repo grep for the old name before merging. Replace every occurrence in one PR. Don't trust that the IDE caught everything. The grep pattern should include both Apex source and metadata XML files; references in formula fields, validation rules, and flow XML are easy to miss because they don't show up in the IDE's reference search.

Further reading from Salesforce

Related dictionary terms

Share this fix

Share on LinkedInShare on X

Related Deployment errors