Introduction
Custom Settings are a lightweight way to hold application-wide configuration, so most of us reach for them early. Marking one 'Protected' buys a specific kind of security: the data stays hidden from managed packages and external orgs. Then a Lightning Web Component queries that setting and the values come back null.
Why the value comes back null
Marking a Custom Setting as protected restricts access to that setting to the scope of the package that owns it. The null you are staring at is usually that restriction doing its job, not a platform bug.
When your LWC calls an @AuraEnabled Apex method to retrieve those values, the context depends on whether your code is part of a soql-in-loops-security-review-impact-for-managed-packages/" class="auto-link">managed package. Develop outside the namespace that owns the setting, or fragment the visibility somewhere in your org structure, and the system returns null silently instead of exposing the configuration.
The common pitfalls
- The component sits outside the managed namespace that owns the setting.
- The LWC runs in a context that lacks the permissions to see the private settings.
- Now and then
getOrgDefaults()caches values wrongly inside a user session, after the hierarchy or scope has changed.
Step 1: check the retrieval pattern first
Before you debug the null, confirm you are using the right retrieval method. Plenty of us have called getInstance() with the wrong Id, or never checked whether the record exists at all.
The standard, secure way to fetch these settings in Apex:
public with sharing class ConfigService {
@AuraEnabled(cacheable=true)
public static Map<String, String> getProtectedSettings() {
// Fetch the setting record
My_Protected_Setting__c settings = My_Protected_Setting__c.getOrgDefaults();
Map<String, String> configMap = new Map<String, String>();
// Always check for null before accessing fields
if (settings != null) {
configMap.put('ApiEndpoint', settings.API_Endpoint__c);
configMap.put('Timeout', String.valueOf(settings.Timeout__c));
}
return configMap;
}
}
If that method returns null for your LWC, you are almost certainly looking at an access violation caused by the 'Protected' scope.
Step 2: architecture options for protected data
If your architecture needs LWC to reach sensitive data, there are three paths out of the null.
1. The wrapper service pattern (recommended)
Rather than exposing the settings directly, create a service layer class that runs without sharing if the business requirements permit, or manage the visibility explicitly. A service class keeps the retrieval logic in one place and puts the null checks on the server, so the LWC never receives undefined data.
2. Custom Metadata Types (CMDT)
If the configuration does not strictly need hiding from managed packages, and is not subject to user-specific overrides, consider switching to Custom Metadata Types. CMDTs are queryable through SOQL and behave more predictably when Apex reads them for an LWC.
3. Permission set validation
Check that the user running the LWC has the access. Even with the setting marked 'Protected', a component running in that user's context needs the 'View All Custom Settings' permission, or whatever specific access your org setup calls for, or the payload arrives empty.
Step 3: debugging and validation
Stop troubleshooting from the LWC console logs alone. The problem usually hides in the Apex layer. Three checks worth running:
- Add a
System.debugimmediately after thegetOrgDefaults()call. If the value is null there, the LWC is not your problem; the visibility is. - Run a block of anonymous Apex as the specific user hitting the issue, using
System.runAs(). That mimics the LWC execution context. - If your organization uses namespaces, check that your Apex class is prefixed correctly when the custom setting belongs to that package.
// Execute as a non-admin user to verify security visibility
User u = [SELECT Id FROM User WHERE Profile.Name = 'Standard User' LIMIT 1];
System.runAs(u) {
My_Protected_Setting__c settings = My_Protected_Setting__c.getOrgDefaults();
System.debug('Settings found: ' + settings);
}
Leave a Comment