Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Apex

System.TypeException: Invalid id: <value>

You cast a String to `Id` but the string isn't a syntactically valid Salesforce ID — wrong length, wrong characters, or just a typo. Different from `INVALID_CROSS_REFERENCE_KEY`, which fires when the ID looks valid but doesn't exist or is the wrong sObject.

Also seen asTypeException: Invalid id·Invalid id apex·System.TypeException: Invalid id

The Apex Id type isn't a free-form string. The runtime validates the format on assignment: 15 or 18 alphanumeric characters, no hyphens, no spaces. Fail validation, you get TypeException, before any DB lookup happens.

Id valid    = '0016x00000ABCDE';        // OK — 15 chars
Id valid18  = '0016x00000ABCDEAA1';     // OK — 18 chars (with checksum)
Id bad      = '0016x00000ABCD';         // ❌ TypeException — 14 chars
Id bad2     = 'foo bar';                // ❌ TypeException — invalid chars
Id bad3     = '00-16x000-00ABCDE';      // ❌ TypeException — has hyphens

The cast happens implicitly any time you assign to an Id variable, pass to a method expecting Id, or use : in SOQL with an Id-typed bind.

Where it usually comes from

  1. A Lightning component passing a record ID that wasn't trimmed, normalised, or properly URL-decoded. recordId from the URL is sometimes %2F-encoded, or has a trailing space.
  2. A custom-button URL parameter copied from the address bar where Salesforce included extra path segments.
  3. A Process Builder / Flow passing a string that isn't an ID at all (e.g., a custom field value).
  4. External-system payloads where the upstream system uses its own ID format and someone confused that with the Salesforce ID.

How to validate before casting

public static Boolean isValidId(String s) {
    if (s == null) return false;
    if (s.length() != 15 && s.length() != 18) return false;
    return Pattern.matches('^[a-zA-Z0-9]+$', s);
}

if (!isValidId(input)) {
    throw new IllegalArgumentException('Not a valid Salesforce Id: ' + input);
}
Id parsed = (Id) input;

Or use Schema.SObjectType to also confirm the prefix matches the sObject you expect:

public static Boolean isAccountId(String s) {
    if (!isValidId(s)) return false;
    Id parsed = (Id) s;
    return parsed.getSObjectType() == Account.SObjectType;
}

This rejects 005xxx... (User) when you wanted 001xxx... (Account), before you try to query.

A subtle case: 15 vs 18

15-character IDs are case-sensitive (the same record is 0016x00000ABCDE only — 0016X00000ABCDE is technically a different ID in the 15-char namespace). 18-character IDs are case-insensitive — the trailing 3 characters encode the case.

If your code uppercases or lowercases an ID before storing it, switching to 18-char form first gets you safe round-tripping:

String id18 = ((Id) input15).to18();   // .to18() is on Id, not String

Related dictionary terms