-
Notifications
You must be signed in to change notification settings - Fork 0
Diagnostics
The SDK exposes three complementary debugging mechanisms: structured diagnostic
logging, typed errors with .code and .context attributes, and *Diagnostic
result objects that describe evaluation decisions without raising. This page
covers all three and walks through what to gather when filing a support ticket.
All internal SDK activity emits structured DEBUG-level log records through the
Python logging module on the logger named convert_sdk.
Enable diagnostic output by setting that logger to DEBUG:
import logging
logging.getLogger("convert_sdk").setLevel(logging.DEBUG)
logging.basicConfig(
level=logging.DEBUG,
format="%(name)s %(levelname)s %(message)s",
)Or in Django settings.py:
LOGGING = {
"version": 1,
"handlers": {"console": {"class": "logging.StreamHandler"}},
"loggers": {
"convert_sdk": {
"handlers": ["console"],
"level": "DEBUG",
"propagate": False,
},
},
}The SDK emits through the logger supplied in SDKConfig.logger (defaults to
logging.getLogger("convert_sdk")). It never calls logging.basicConfig(),
adds handlers, or sets levels — that is the application's responsibility.
Diagnostic logs never emit raw visitor ids, SDK keys, secrets, cookies, or raw
attribute mappings. Sensitive values are replaced with "<redacted>". Visitor
ids are replaced with a hashed 16-character visitor_ref prefix.
All SDK errors derive from ConvertSDKError and carry structured metadata:
class ConvertSDKError(Exception):
code: str | None # machine-readable error code
context: Mapping[str, Any] # structured metadata (immutable)ConvertSDKError (base)
├── ConfigError
│ ├── InvalidConfigError (code: "config.invalid")
│ └── ConfigLoadError (code: "config_load_failed")
├── TransportError (code varies)
└── TrackingDeliveryError (code: "tracking_delivery_failed")
# Not top-level exports — import from convert_sdk.errors:
TrackingError (programmer-misuse base)
└── ConversionDataError (invalid conversion_data value)
Example structured error handling:
from convert_sdk import ConfigLoadError
try:
core = Core(SDKConfig(sdk_key=os.environ["CONVERT_SDK_KEY"])).initialize()
except ConfigLoadError as exc:
print(exc.code) # "config_load_failed"
print(dict(exc.context)) # {"endpoint": "...", "status_code": ...}Unknown goals are not exceptions. track_conversion always returns a typed
ConversionResult; a missing goal is reflected as
ConversionStatus.GOAL_NOT_FOUND:
from convert_sdk import ConversionStatus
result = context.track_conversion("unknown-goal")
if result.status is ConversionStatus.GOAL_NOT_FOUND:
print(result.reason) # "goal_not_found"
print(result.tracked) # FalseTo inspect why a goal is missing, use the diagnostic surface:
from convert_sdk import DiagnosticReason
diag = context.diagnose_goal("unknown-goal")
print(diag.reason) # DiagnosticReason.GOAL_NOT_FOUND
print(diag.message) # human-readable explanationInstead of calling run_experience() (which returns ExperienceResult | None),
call diagnose_experience() to get the full decision record without raising:
from convert_sdk import DiagnosticReason
diag = context.diagnose_experience("checkout-experiment")
print(diag.resolved) # bool — True when bucketed
print(diag.reason) # DiagnosticReason enum value
print(diag.message) # human-readable description
print(dict(diag.details)) # redaction-safe operational fieldsThe same pattern applies to features, goals, and arbitrary config entities:
feat_diag = context.diagnose_feature("checkout-banner")
goal_diag = context.diagnose_goal("purchase_completed")
entity_diag = context.diagnose_entity("experience", "checkout-experiment")See Type Hints for the field-by-field reference of each diagnostic class.
DiagnosticReason |
Value | Meaning |
|---|---|---|
RESOLVED |
"resolved" |
The request resolved to a concrete outcome |
AUDIENCE_MISMATCH |
"audience_mismatch" |
Visitor did not satisfy audience/location rules |
EXPERIENCE_NOT_FOUND |
"experience_not_found" |
No experience matched the requested key |
FEATURE_NOT_IN_SELECTED_VARIATIONS |
"feature_not_in_selected_variations" |
Feature declared but visitor's variation carries no change for it |
FEATURE_NOT_FOUND |
"feature_not_found" |
No feature matched the requested key |
GOAL_NOT_FOUND |
"goal_not_found" |
No goal matched the requested key |
ENTITY_NOT_FOUND |
"entity_not_found" |
Config-entity lookup found no match |
PROJECT_MAPPING_REQUIRED |
"project_mapping_required" |
Loaded config lacks required project mapping |
The ExperienceDiagnostic.details dict includes operational fields like
bucket_value when bucketing was attempted. The bucket value is deterministic
across all Convert SDKs for the same (visitor_id, experience_id) pair — you
can compare the Python SDK's bucket_value to the JavaScript SDK's output to
verify parity.
The parity test suite in the python-sdk repository under tests/parity/
exercises this contract with shared test vectors.
To re-derive a bucket value by hand:
from convert_sdk.evaluation.bucketing import get_bucket_value_for_visitor
bucket = get_bucket_value_for_visitor(
visitor_id="visitor-abc123",
experience_id="exp-checkout",
)
print("bucket_value:", bucket) # compare to JS SDK outputThe Python SDK uses a pure-Python MurmurHash3 32-bit implementation with seed
9999. The hash input is always f"{experience_id}{visitor_id}" (experience id
first). The JavaScript and PHP SDKs use the same algorithm and input order.
import os
from convert_sdk import Core, SDKConfig, ConfigLoadError
try:
core = Core(SDKConfig(sdk_key=os.environ["CONVERT_SDK_KEY"])).initialize()
except ConfigLoadError as exc:
print("code:", exc.code)
print("context:", dict(exc.context))
print("is_ready:", core.is_ready)
snapshot = core.current_config
if snapshot is not None:
print("experiences:", len(snapshot.experiences_by_key))
print("features:", len(snapshot.features_by_key))
print("goals:", len(snapshot.goals_by_key))If core.is_ready is True but entity counts are zero, the config was loaded
but is empty — check whether the correct environment was requested.
If events are not appearing in the Convert dashboard:
- Confirm
core.flush()is called before the process exits or the request completes. - Subscribe to
LifecycleEvent.API_QUEUE_RELEASEDto capture delivery errors (see Code Examples — Queue Control). - Check
TrackingDeliveryError.status_codeif a delivery exception was raised. - Confirm that
sdk_keyis present — the transport requires it for the tracking route.
Collect the following before opening an issue:
-
SDK version —
python -c "import convert_sdk; print(convert_sdk.__version__)" -
Python version —
python --version - Initialization mode — SDK key or direct config?
-
Diagnostic result — For evaluation or tracking failures, use the
diagnose_*methods and include the fullreason,message, anddetailsfields. -
Error details — If an exception was raised, include
exc.codeanddict(exc.context). - Privacy note — Raw visitor ids and SDK keys are automatically redacted in diagnostic logs; do not manually re-add them to your bug report.
When filing a bug, include a minimal standalone script that reproduces the issue using direct config mode (no network dependency):
from convert_sdk import Core, SDKConfig
config = {
"account_id": "YOUR_ACCOUNT_ID",
"project": {"id": "YOUR_PROJECT_ID", "name": "Repro"},
"experiences": [
# paste the minimal experience definition from your config
],
"features": [],
"goals": [],
}
core = Core(SDKConfig(data=config)).initialize()
context = core.create_context("repro-visitor-1", visitor_attributes={"tier": "premium"})
diag = context.diagnose_experience("your-experience-key")
print(diag.resolved, diag.reason, dict(diag.details))
core.close()Using data instead of sdk_key avoids network dependencies and
confidential key exposure in bug reports.
-
Type Hints — full reference for
*Diagnosticand result types - Code Examples — Queue Control — lifecycle events for delivery monitoring
- Extending — replace the logger or transport for custom observability
- Troubleshooting — common cross-SDK issues and fixes
Copyrights © 2025 All Rights Reserved by Convert Insights, Inc.
Getting Started
Python SDK
- Quickstart
- Installation
- Initialization
- Configuration
- Code Examples
- Type Hints
- Diagnostics
- Extending
- Testing
- Async & Frameworks
Migration
Core Concepts
- Experiences & Variations
- Feature Flags
- Bucketing Algorithm
- Rule Evaluation
- Segments
- Data Management
- Event System
- API Communication
How-To Guides
- Running Experiences
- Running Features
- Tracking Conversions
- Visitor Context
- Persistent DataStore
- Troubleshooting
Edge & Integrations
Maintainers