OpenTelemetry Exporter Configuration: JS vs. Python Endpoint Handling#
PHOENIX_COLLECTOR_ENDPOINT is interpreted differently by Python and JavaScript SDKs. This mismatch causes silent trace loss for any JS user who follows documentation or examples written for the Python behavior.
The Core Difference#
| SDK | Behavior |
|---|---|
Python (phoenix-otel, phoenix.trace) | Treats PHOENIX_COLLECTOR_ENDPOINT as a base URL and appends /v1/traces automatically |
JavaScript (@arizeai/openinference-mastra) | POSTs to the endpoint exactly as given β no path is appended |
Python (src/phoenix/trace/exporter.py): The _OpenInferenceExporter class reads PHOENIX_COLLECTOR_ENDPOINT via get_env_collector_endpoint(), ensures a trailing slash, then calls urljoin(base_url, "v1/traces") before passing the result to OTLPSpanExporter .
The phoenix-otel package does the same β _construct_http_endpoint() always replaces the URL path with /v1/traces, and _normalized_endpoint() applies this transformation when the env var is read .
JavaScript (@arizeai/openinference-mastra): OpenInferenceOTLPTraceExporter is a thin wrapper around @opentelemetry/exporter-trace-otlp-proto's OTLPTraceExporter. The constructor accepts a url option and passes it through with no modification . The README shows the correct usage pattern β passing the URL directly from the env var:
url: process.env.PHOENIX_COLLECTOR_ENDPOINT,
The README documentation explicitly sets PHOENIX_COLLECTOR_ENDPOINT to the full URL including /v1/traces :
export PHOENIX_COLLECTOR_ENDPOINT="https://localhost:6006/v1/traces"
The Failure Mode#
When a user sets PHOENIX_COLLECTOR_ENDPOINT=http://localhost:6006 (the Python convention), the JS exporter POSTs to the base URL. Phoenix has no handler at /, the request fails, and the batch exporter silently swallows the delivery error β the application looks healthy throughout .
This is especially dangerous in containerized environments, where the collector endpoint is typically set to a bare host by construction (e.g., http://phoenix:6006). Every Mastra run will fail identically and silently until the endpoint is corrected .
The issue was first confirmed during onboarding testing against the no-observability/mastra repository .
Fix#
Immediate fix: Set PHOENIX_COLLECTOR_ENDPOINT to the full OTLP path when using a JS exporter:
# For JS/Mastra (include the path)
PHOENIX_COLLECTOR_ENDPOINT=http://localhost:6006/v1/traces
# For Python (base URL only β path is appended automatically)
PHOENIX_COLLECTOR_ENDPOINT=http://localhost:6006
Planned fix: Normalize the endpoint inside the JS exporter itself, so it checks for the /v1/traces suffix and appends it if missing. A cheaper interim path is having px setup write the suffixed endpoint when it detects a JS target .
Related Issues#
- #14875 β Tracking issue for the exporter normalization fix
- #14876 β
px setupverification may report success without a landed trace - #14877 β Project-name redirect shows a bare error when the project doesn't exist
Key Source Files#
| File | Purpose |
|---|---|
src/phoenix/trace/exporter.py | Python _OpenInferenceExporter β appends /v1/traces via urljoin |
packages/phoenix-otel/src/phoenix/otel/otel.py | phoenix-otel endpoint normalization β _construct_http_endpoint |
js/packages/openinference-mastra/src/OpenInferenceTraceExporter.ts | JS OpenInferenceOTLPTraceExporter β passes URL through unchanged |
js/packages/openinference-mastra/README.md | Correct JS configuration example (full URL required) |