The PlatformCache paradox
If you build modular solutions or managed packages, you have probably run into the PlatformCache namespace limits. The strangest one shows up when you call put on Cache.Org or Cache.Session. The immutable parameter, which is supposed to let us control whether cached data can be modified, behaves in ways the documentation does not describe. It fails for a documented platform reason, and the way around it is to stop calling that overload.
The anatomy of the bug
Working inside a namespace, developers set the immutable flag to false so the data can still be updated. The signature part.put(key, value, ttl, visibility, immutable) reads like a promise: pass false as the final argument and the platform keeps that cached value mutable.
In current Salesforce versions you get a persistent runtime error instead:
// Attempting to use the immutable parameter
Cache.SessionPartition part = Cache.Session.getPartition('local.MyPartition');
try {
part.put('configKey', 'someValue', 3600, Cache.Visibility.ALL, false);
} catch (Cache.Org.OrgCacheException e) {
// Result: Failed Cache.Session.put() for key 'configKey': Cross-namespace PUT not supported
System.debug('Error: ' + e.getMessage());
}
Your logic is fine. The Salesforce engineering team has recognized the behavior as a bug (Known Issue a02Ka00000mmTqd). Set immutable to false and the platform still reads the call as an attempt to perform a cross-namespace PUT, which it restricts for security and multi-tenancy isolation.
Why 'immutable' fails
The immutable parameter was meant to prevent race conditions and unauthorized modification of cached objects. Setting the flag creates a read-only contract for that one cache entry.
The conflict sits between namespace boundaries and the storage engine underneath Platform Cache. When a package tries to modify a value in a partition the platform views as owned by a different scope, it throws a security exception. The value you passed for immutable never gets read, because the restriction on cross-namespace writing is evaluated before the mutation policy is applied.
Practical workarounds and best practices
This is a live platform bug, so immutable will not get you out of a cross-namespace storage problem. Architect your caching strategy to be namespace-agnostic or strictly self-contained instead.
1. Avoid cross-namespace dependency
If your soql-in-loops-security-review-impact-for-managed-packages/" class="auto-link">managed package needs to cache data, define the partition inside your own namespace, or use a common namespace your organization controls. Do not write to partitions defined in other packages.
2. Implement a 'get-or-compute' pattern
Rather than forcing an update onto an existing cache key that might belong to another scope, use a get-or-compute pattern that handles a cache miss gracefully.
public static Object getCachedData(String key) {
Cache.SessionPartition part = Cache.Session.getPartition('local.MyPartition');
Object cachedValue = part.get(key);
if (cachedValue == null) {
// Compute the data if not in cache
cachedValue = performComplexCalculation();
try {
// Use the default immutable parameter (true) to avoid the bug
part.put(key, cachedValue, 3600);
} catch (Exception e) {
// Fallback for cache failure
return cachedValue;
}
}
return cachedValue;
}
3. Use default method signatures
Stay off the put overload that takes the immutable parameter. The overloads that do not expose the flag skip the logic that leads to the Cross-namespace PUT error.
Troubleshooting checklist
- Check whether the partition you are calling lives in your package's namespace or in the
localnamespace. - Use
Cache.Visibility.ALLonly when you strictly need it.Cache.Visibility.NAMESPACEtightens security and avoids cross-scope collisions. - Check the Salesforce Known Issues site now and then for movement on the ticket linked in the research section.
Leave a Comment