Troubleshooting

Common errors and how to fix them

Common Errors

SDK Initialization Failed

  • Cause (JS): Incorrect sdkKey or network issues
  • Cause (PHP): Incorrect sdkKey, network issues, or no PSR-18 HTTP client installed
  • Cause (Android): Incorrect sdkKey or network issues — on a failed config fetch the SDK loads the last cached config from app-private storage; with no cache, decision methods return null until the next successful fetch
  • Cause (Ruby): Incorrect sdk_key (or sdk_key_secret) or network issues — a failed config fetch is logged at warn and decision methods return sentinels rather than raising; ConvertSdk.create itself raises ArgumentError only on misconfiguration (for example, neither sdk_key: nor data: supplied)
  • Cause (iOS): Incorrect sdkKey (or sdkKeySecret) or network issues — on a failed config fetch the SDK loads the last-good config cached on disk (Application Support); with no cache, runExperience returns nil and runFeature returns a disabled Feature until the next successful fetch. ready() is the only throwing call, and it throws only on an unrecoverable config error (empty sdkKey, or empty/invalid direct configData)
  • Cause (Python): Incorrect sdk_key or network issues — unlike the mobile SDKs, Core(SDKConfig(...)).initialize() is fail-fast with no on-disk last-good cache: a malformed config raises InvalidConfigError and a failed fetch raises ConfigLoadError (both subclasses of ConfigError). Wrap initialize() in try / except ConfigError and decide your fallback. Direct-data init makes no network call.
  • Solution: Verify the sdkKey and ensure network connectivity. If using an authenticated key, ensure sdkKeySecret is 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/discovery cannot 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/guzzle or 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 userId string is provided to createContext().
  • Solution (PHP): Ensure a valid, non-empty visitorId string is provided to createContext(). Check that isReady() returns true before creating contexts.
  • Solution (Android): createContext() with no arguments auto-generates and persists a UUID visitor id. Decision calls made before the SDK is ready return null (never throw) — gate them behind onReady { }.
  • Solution (Ruby): A blank or nil visitor_id makes create_context return nil (with an error log) rather than raising — guard for nil before using the context. Subscribe to the ready event (or pass static data:) so contexts are created against an installed config.
  • Solution (iOS): createContext(visitorId:attributes:) with no visitorId auto-generates and persists a Keychain UUID. Decision calls made before try await sdk.ready() return nil / disabled features (never crash) — await sdk.ready() first.
  • Solution (Python): create_context(visitor_id, ...) raises RuntimeError if called before initialize(). Because initialize() is synchronous and returns a ready Core, there is no "created before ready" race to guard against — initialize first, then create contexts. Pass a stable, non-empty visitor_id for deterministic bucketing.

Experience Not Found

  • Cause: Incorrect experienceKey or experience not defined in the project
  • Solution: Verify the experienceKey matches exactly. Ensure the experience is active (not draft or paused) and belongs to the correct environment.

Feature Not Found

  • Cause: Incorrect featureKey or feature not linked to any experience
  • Solution: Verify the featureKey. Ensure at least one active experience has a variation with a fullStackFeature change 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 visitorProperties and locationProperties passed to the method match the rules configured in Convert. See Rule Evaluation & Targeting for how rules are evaluated.
  • Solution (PHP): Check that the visitorProperties and locationProperties in BucketingAttributes match the rules configured in Convert. The RuleError enum has two cases:
    • RuleError::NoDataFound -- no matching data for the rule
    • RuleError::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 a nil #key), or check variation.error?. The sentinels are RuleError::NO_DATA_FOUND, RuleError::NEED_MORE_DATA, and BucketingError::VARIATION_NOT_DECIDED. Check that the visitor_properties / location_properties you pass match the rules configured in Convert.
  • Solution (iOS): A miss returns nil from runExperience (and a disabled Feature from runFeature), 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 scalar attributes you pass to createContext match the rules configured in Convert; only String/Int/Double/Bool values are kept (nested values are dropped).
  • Solution (Python): A miss returns None from run_experience and run_feature (and an empty list from run_experiences / run_features), never an error. For the why without guessing, call context.diagnose_experience(key) / diagnose_feature(key) — they return a typed DiagnosticReason (e.g. AUDIENCE_MISMATCH, EXPERIENCE_NOT_FOUND) and never raise on a miss. Check that the attributes / location_attributes you 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.tracking is not false in the config. Call releaseQueues() to flush pending events. Verify the SDK key. Check that enableTracking is not false in method attributes.
  • Solution (PHP): Ensure network.tracking is not false in the config. Call releaseQueues() or flush() 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 call flush() explicitly.
  • Solution (Android): Check that tracking wasn't disabled via trackingEnabled(false) at build time, setTrackingEnabled(false) at runtime, or enableTracking = false on the call. Offline events are queued durably and ship when connectivity returns — declare ACCESS_NETWORK_STATE so the SDK can flush immediately on reconnect.
  • Solution (Ruby): Ensure tracking is not false in the config (the global switch always wins over a per-call track). Call flush (alias release_queues) to send pending events. The PID-guarded at_exit handler flushes on process exit, but when flush_interval is set to nil (Lambda / CLI) you must call flush explicitly. Verify enable_tracking is not false in the call attributes.
  • Solution (iOS): Ensure tracking isn't disabled at any of the three layers — static ConvertConfiguration.networkTracking = false, runtime await sdk.setTrackingEnabled(false), or per-call enableTracking: false. Offline events are queued durably in Application Support and ship when connectivity returns; a background URLSession also completes delivery after the app is suspended.
  • Solution (Python): track_conversion (and the bucketing/activation event enqueued by run_experience / run_experiences) only enqueues — it never sends over the network. Events deliver when batch_size (default 10) fills, when the opt-in auto_flush_interval_ms timer fires, on a best-effort atexit hook, or when you call core.flush(). In short-lived runtimes (Lambda / CLI) leave auto_flush_interval_ms unset and call core.flush() explicitly before exit. If experiment exposures aren't appearing, verify you didn't pass enable_tracking=False on the run call — it suppresses the bucketing event per call (default is True). A ConversionStatus.GOAL_NOT_FOUND result (not an exception) means the goal key is absent from the loaded config — verify the sdk_key and 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 using sdkKey, the CDN response may be temporarily invalid -- the SDK will throw a ConfigValidationException.