Troubleshooting
Common errors and how to fix them
Common Errors
SDK Initialization Failed
- Cause (JS): Incorrect
sdkKeyor network issues - Cause (PHP): Incorrect
sdkKey, network issues, or no PSR-18 HTTP client installed - Cause (Android): Incorrect
sdkKeyor network issues — on a failed config fetch the SDK loads the last cached config from app-private storage; with no cache, decision methods returnnulluntil the next successful fetch - Cause (Ruby): Incorrect
sdk_key(orsdk_key_secret) or network issues — a failed config fetch is logged atwarnand decision methods return sentinels rather than raising;ConvertSdk.createitself raisesArgumentErroronly on misconfiguration (for example, neithersdk_key:nordata:supplied) - Cause (iOS): Incorrect
sdkKey(orsdkKeySecret) or network issues — on a failed config fetch the SDK loads the last-good config cached on disk (Application Support); with no cache,runExperiencereturnsnilandrunFeaturereturns a disabledFeatureuntil the next successful fetch.ready()is the only throwing call, and it throws only on an unrecoverable config error (emptysdkKey, or empty/invalid directconfigData) - Cause (Python): Incorrect
sdk_keyor network issues — unlike the mobile SDKs,Core(SDKConfig(...)).initialize()is fail-fast with no on-disk last-good cache: a malformed config raisesInvalidConfigErrorand a failed fetch raisesConfigLoadError(both subclasses ofConfigError). Wrapinitialize()intry/except ConfigErrorand decide your fallback. Direct-datainit makes no network call. - Solution: Verify the
sdkKeyand ensure network connectivity. If using an authenticated key, ensuresdkKeySecretis also provided. (PHP only) Install a PSR-18 HTTP client (e.g.,composer require guzzlehttp/guzzle).
No PSR-18 HTTP Client Found (PHP only)
- Cause:
php-http/discoverycannot find a PSR-18 HTTP client implementation - Error message:
No PSR-18 HTTP client found. Install one (e.g., guzzlehttp/guzzle ^7) or pass an explicit httpClient. - Solution: Install one:
composer require guzzlehttp/guzzleor any other PSR-18 compatible client.
User Context Creation Failed
- Cause (JS): Missing or invalid
userId - Cause (PHP): Missing or empty
visitorId, or SDK not initialized - Solution (JS): Ensure a valid, non-empty
userIdstring is provided tocreateContext(). - Solution (PHP): Ensure a valid, non-empty
visitorIdstring is provided tocreateContext(). Check thatisReady()returnstruebefore creating contexts. - Solution (Android):
createContext()with no arguments auto-generates and persists a UUID visitor id. Decision calls made before the SDK is ready returnnull(never throw) — gate them behindonReady { }. - Solution (Ruby): A blank or
nilvisitor_idmakescreate_contextreturnnil(with an error log) rather than raising — guard fornilbefore using the context. Subscribe to thereadyevent (or pass staticdata:) so contexts are created against an installed config. - Solution (iOS):
createContext(visitorId:attributes:)with novisitorIdauto-generates and persists a Keychain UUID. Decision calls made beforetry await sdk.ready()returnnil/ disabled features (never crash) —await sdk.ready()first. - Solution (Python):
create_context(visitor_id, ...)raisesRuntimeErrorif called beforeinitialize(). Becauseinitialize()is synchronous and returns a readyCore, there is no "created before ready" race to guard against — initialize first, then create contexts. Pass a stable, non-emptyvisitor_idfor deterministic bucketing.
Experience Not Found
- Cause: Incorrect
experienceKeyor experience not defined in the project - Solution: Verify the
experienceKeymatches exactly. Ensure the experience isactive(notdraftorpaused) and belongs to the correctenvironment.
Feature Not Found
- Cause: Incorrect
featureKeyor feature not linked to any experience - Solution: Verify the
featureKey. Ensure at least one active experience has a variation with afullStackFeaturechange linked to this feature.
Visitor Not Bucketed (RuleError)
- Cause: The visitor's properties or location don't match the experience's audience/location rules
- Solution (JS): Check that the
visitorPropertiesandlocationPropertiespassed to the method match the rules configured in Convert. See Rule Evaluation & Targeting for how rules are evaluated. - Solution (PHP): Check that the
visitorPropertiesandlocationPropertiesinBucketingAttributesmatch the rules configured in Convert. TheRuleErrorenum has two cases:RuleError::NoDataFound-- no matching data for the ruleRuleError::NeedMoreData-- insufficient data provided to evaluate the rule
- Solution (Ruby): A miss returns a frozen sentinel instead of a variation — detect it with
case variation.key when nil(every sentinel has anil#key), or checkvariation.error?. The sentinels areRuleError::NO_DATA_FOUND,RuleError::NEED_MORE_DATA, andBucketingError::VARIATION_NOT_DECIDED. Check that thevisitor_properties/location_propertiesyou pass match the rules configured in Convert. - Solution (iOS): A miss returns
nilfromrunExperience(and a disabledFeaturefromrunFeature), never an error — a rule mismatch, an inactive or unknown experience, an ineligible visitor, or a not-yet-ready SDK all yield a miss. Check that the scalarattributesyou pass tocreateContextmatch the rules configured in Convert; onlyString/Int/Double/Boolvalues are kept (nested values are dropped). - Solution (Python): A miss returns
Nonefromrun_experienceandrun_feature(and an empty list fromrun_experiences/run_features), never an error. For the why without guessing, callcontext.diagnose_experience(key)/diagnose_feature(key)— they return a typedDiagnosticReason(e.g.AUDIENCE_MISMATCH,EXPERIENCE_NOT_FOUND) and never raise on a miss. Check that theattributes/location_attributesyou pass match the rules configured in Convert.
Tracking Events Not Appearing
- Cause: Tracking is disabled, events are still queued, or the SDK key is invalid
- Solution (JS): Ensure
network.trackingis notfalsein the config. CallreleaseQueues()to flush pending events. Verify the SDK key. Check thatenableTrackingis notfalsein method attributes. - Solution (PHP): Ensure
network.trackingis notfalsein the config. CallreleaseQueues()orflush()to send pending events. Verify the SDK key. The shutdown handler should flush automatically in PHP-FPM, but in CLI scripts you may need to callflush()explicitly. - Solution (Android): Check that tracking wasn't disabled via
trackingEnabled(false)at build time,setTrackingEnabled(false)at runtime, orenableTracking = falseon the call. Offline events are queued durably and ship when connectivity returns — declareACCESS_NETWORK_STATEso the SDK can flush immediately on reconnect. - Solution (Ruby): Ensure
trackingis notfalsein the config (the global switch always wins over a per-call track). Callflush(aliasrelease_queues) to send pending events. The PID-guardedat_exithandler flushes on process exit, but whenflush_intervalis set tonil(Lambda / CLI) you must callflushexplicitly. Verifyenable_trackingis notfalsein the call attributes. - Solution (iOS): Ensure tracking isn't disabled at any of the three layers — static
ConvertConfiguration.networkTracking = false, runtimeawait sdk.setTrackingEnabled(false), or per-callenableTracking: false. Offline events are queued durably in Application Support and ship when connectivity returns; a backgroundURLSessionalso completes delivery after the app is suspended. - Solution (Python):
track_conversion(and the bucketing/activation event enqueued byrun_experience/run_experiences) only enqueues — it never sends over the network. Events deliver whenbatch_size(default 10) fills, when the opt-inauto_flush_interval_mstimer fires, on a best-effortatexithook, or when you callcore.flush(). In short-lived runtimes (Lambda / CLI) leaveauto_flush_interval_msunset and callcore.flush()explicitly before exit. If experiment exposures aren't appearing, verify you didn't passenable_tracking=Falseon the run call — it suppresses the bucketing event per call (default isTrue). AConversionStatus.GOAL_NOT_FOUNDresult (not an exception) means the goal key is absent from the loaded config — verify thesdk_keyand the goal key.
Config Validation Failed (PHP only)
- Cause: The fetched or provided project configuration is malformed
- Solution: If using static
data, verify the structure matches the Convert Serving API format. If usingsdkKey, the CDN response may be temporarily invalid -- the SDK will throw aConfigValidationException.