Exceptions¶
All library exceptions inherit from MixpanelHeadlessError, enabling callers to catch all library errors with a single except clause.
Explore on DeepWiki
Ask questions about specific exceptions, error recovery patterns, or debugging strategies.
Exception Hierarchy¶
MixpanelHeadlessError
├── ConfigError
│ ├── AccountNotFoundError
│ ├── AccountExistsError
│ ├── AccountInUseError
│ ├── InvalidArgumentError
│ └── ProjectNotFoundError
├── APIError
│ ├── AuthenticationError
│ ├── RateLimitError
│ ├── QueryError
│ ├── ServerError
│ └── JQLSyntaxError
├── OAuthError
│ └── RegionProbeError
│ └── RegionProbeNetworkError
├── WorkspaceScopeError
└── BusinessContextValidationError
Catching Errors¶
import mixpanel_headless as mp
try:
ws = mp.Workspace()
result = ws.segmentation(event="Purchase", from_date="2025-01-01", to_date="2025-01-31")
except mp.AuthenticationError as e:
print(f"Auth failed: {e.message}")
except mp.RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except mp.OAuthError as e:
print(f"OAuth error [{e.code}]: {e.message}")
except mp.WorkspaceScopeError as e:
print(f"Workspace error [{e.code}]: {e.message}")
except mp.AccountInUseError as e:
print(f"Account '{e.account_name}' referenced by targets: {e.referenced_by}")
except mp.MixpanelHeadlessError as e:
print(f"Error [{e.code}]: {e.message}")
Base Exception¶
mixpanel_headless.MixpanelHeadlessError
¶
MixpanelHeadlessError(
message: str,
code: str = "UNKNOWN_ERROR",
details: dict[str, Any] | None = None,
)
Bases: Exception
Base exception for all mixpanel_headless errors.
All library exceptions inherit from this class, allowing callers to: - Catch all library errors: except MixpanelHeadlessError - Handle specific errors: except AccountNotFoundError - Serialize errors: error.to_dict()
Initialize exception.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
code
|
Machine-readable error code for programmatic handling.
TYPE:
|
details
|
Additional structured data about the error.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
API Exceptions¶
mixpanel_headless.APIError
¶
APIError(
message: str,
*,
status_code: int,
response_body: str | dict[str, Any] | None = None,
request_method: str | None = None,
request_url: str | None = None,
request_params: dict[str, Any] | None = None,
request_body: dict[str, Any] | None = None,
code: str = "API_ERROR",
)
Bases: MixpanelHeadlessError
Base class for Mixpanel API HTTP errors.
Provides structured access to HTTP request/response context for debugging and automated recovery by AI agents. All API-related exceptions inherit from this class, enabling agents to:
- Understand what went wrong (status code, error message)
- See exactly what was sent (request method, URL, params, body)
- See exactly what came back (response body, headers)
- Modify their approach and retry autonomously
Example
Initialize APIError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
status_code
|
HTTP status code from response.
TYPE:
|
response_body
|
Raw response body (string or parsed dict).
TYPE:
|
request_method
|
HTTP method used (GET, POST).
TYPE:
|
request_url
|
Full request URL.
TYPE:
|
request_params
|
Query parameters sent.
TYPE:
|
request_body
|
Request body sent (for POST requests).
TYPE:
|
code
|
Machine-readable error code.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
response_body
property
¶
Raw response body (string or parsed dict).
mixpanel_headless.AuthenticationError
¶
AuthenticationError(
message: str = "Authentication failed",
*,
status_code: int = 401,
response_body: str | dict[str, Any] | None = None,
request_method: str | None = None,
request_url: str | None = None,
request_params: dict[str, Any] | None = None,
)
Bases: APIError
Authentication with Mixpanel API failed (HTTP 401).
Raised when credentials are invalid, expired, or lack required permissions. Inherits from APIError to provide full request/response context.
Example
Initialize AuthenticationError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
status_code
|
HTTP status code (default 401).
TYPE:
|
response_body
|
Raw response body.
TYPE:
|
request_method
|
HTTP method used.
TYPE:
|
request_url
|
Full request URL.
TYPE:
|
request_params
|
Query parameters sent.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.RateLimitError
¶
RateLimitError(
message: str = "Rate limit exceeded",
*,
retry_after: int | None = None,
status_code: int = 429,
response_body: str | dict[str, Any] | None = None,
request_method: str | None = None,
request_url: str | None = None,
request_params: dict[str, Any] | None = None,
)
Bases: APIError
Mixpanel API rate limit exceeded (HTTP 429).
Raised when the API returns a 429 status. The retry_after property indicates when the request can be retried. Inherits from APIError to provide full request context for debugging.
Example
Initialize RateLimitError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
retry_after
|
Seconds until retry is allowed (from Retry-After header).
TYPE:
|
status_code
|
HTTP status code (default 429).
TYPE:
|
response_body
|
Raw response body.
TYPE:
|
request_method
|
HTTP method used.
TYPE:
|
request_url
|
Full request URL.
TYPE:
|
request_params
|
Query parameters sent.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.QueryError
¶
QueryError(
message: str = "Query execution failed",
*,
status_code: int = 400,
response_body: str | dict[str, Any] | None = None,
request_method: str | None = None,
request_url: str | None = None,
request_params: dict[str, Any] | None = None,
request_body: dict[str, Any] | None = None,
)
Bases: APIError
Query execution failed (HTTP 400 or query-specific error).
Raised when an API query fails due to invalid parameters, syntax errors, or other query-specific issues. Inherits from APIError to provide full request/response context for debugging.
Example
Initialize QueryError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
status_code
|
HTTP status code (default 400).
TYPE:
|
response_body
|
Raw response body with error details.
TYPE:
|
request_method
|
HTTP method used.
TYPE:
|
request_url
|
Full request URL.
TYPE:
|
request_params
|
Query parameters sent.
TYPE:
|
request_body
|
Request body sent (for POST).
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.ServerError
¶
ServerError(
message: str = "Server error",
*,
status_code: int = 500,
response_body: str | dict[str, Any] | None = None,
request_method: str | None = None,
request_url: str | None = None,
request_params: dict[str, Any] | None = None,
request_body: dict[str, Any] | None = None,
)
Bases: APIError
Mixpanel server error (HTTP 5xx).
Raised when the Mixpanel API returns a server error. These are typically transient issues that may succeed on retry. The response_body property contains the full error details from Mixpanel, which often include actionable information (e.g., "unit and interval both specified").
Example
Initialize ServerError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
status_code
|
HTTP status code (5xx).
TYPE:
|
response_body
|
Raw response body with error details.
TYPE:
|
request_method
|
HTTP method used.
TYPE:
|
request_url
|
Full request URL.
TYPE:
|
request_params
|
Query parameters sent.
TYPE:
|
request_body
|
Request body sent (for POST).
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.JQLSyntaxError
¶
Bases: QueryError
JQL script execution failed with syntax or runtime error (HTTP 412).
Raised when a JQL script fails to execute due to syntax errors, type errors, or other JavaScript runtime issues. Provides structured access to error details from Mixpanel's response.
Inherits from QueryError (and thus APIError) to provide full HTTP context.
Example
Initialize JQLSyntaxError.
| PARAMETER | DESCRIPTION |
|---|---|
raw_error
|
Raw error string from Mixpanel API response.
TYPE:
|
script
|
The JQL script that caused the error.
TYPE:
|
request_path
|
API request path from error response.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
Configuration Exceptions¶
mixpanel_headless.ConfigError
¶
Bases: MixpanelHeadlessError
Base for configuration-related errors.
Raised when there's a problem with configuration files, environment variables, or credential resolution.
Initialize ConfigError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
details
|
Additional structured data.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.AccountNotFoundError
¶
Bases: ConfigError
Named account does not exist in configuration.
Raised when attempting to access an account that hasn't been configured. The available_accounts property lists valid account names to help users.
Initialize AccountNotFoundError.
| PARAMETER | DESCRIPTION |
|---|---|
account_name
|
The requested account name that wasn't found.
TYPE:
|
available_accounts
|
List of valid account names for suggestions.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.AccountExistsError
¶
Bases: ConfigError
Account name already exists in configuration.
Raised when attempting to add an account with a name that's already in use.
Initialize AccountExistsError.
| PARAMETER | DESCRIPTION |
|---|---|
account_name
|
The conflicting account name.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.AccountInUseError
¶
Bases: ConfigError
Account is referenced by one or more targets and cannot be removed.
Raised by mp.accounts.remove(name) when the account is referenced by
one or more [targets.NAME] blocks and the caller did not pass
force=True. The list of dependent target names is available in
referenced_by so callers can show a helpful error message or pass
force=True to delete the account and orphan the targets.
Initialize AccountInUseError.
| PARAMETER | DESCRIPTION |
|---|---|
account_name
|
The account that callers tried to remove.
TYPE:
|
referenced_by
|
Names of targets that reference the account.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.ProjectNotFoundError
¶
Bases: ConfigError
Raised when a specified project is not accessible.
Includes the requested project ID and optionally a list of accessible project IDs to help the user correct their selection.
Example
try:
projects = ws.projects()
match = [p for p in projects if p.id == target_id]
if not match:
raise ProjectNotFoundError(
target_id,
available_projects=[p.id for p in projects],
)
except ProjectNotFoundError as e:
print(f"Project '{e.project_id}' not found.")
if e.available_projects:
print(f"Available: {', '.join(e.available_projects)}")
Initialize ProjectNotFoundError.
| PARAMETER | DESCRIPTION |
|---|---|
project_id
|
The requested project ID that wasn't found.
TYPE:
|
available_projects
|
List of accessible project IDs for suggestions.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
InvalidArgumentError¶
Raised by accounts.login_unified (and the CLI's mp login) when a public-API call combines mutually incompatible arguments. Subclass of ConfigError. The CLI maps this to exit code 3 (INVALID_ARGS) instead of the generic 1.
violation |
Raised When |
|---|---|
mutually_exclusive |
--service-account + --token-env (or equivalent kwargs) |
no_browser_misuse |
--no-browser against a non-browser auth type |
secret_stdin_misuse |
--secret-stdin against a non-SA auth type |
The details dict carries violation and (when detection ran) detected_auth_type. Pattern-match by class so non-CLI callers (Cowork's auth_manager.py, JSON consumers) can dispatch without parsing the human message.
mixpanel_headless.InvalidArgumentError
¶
InvalidArgumentError(
message: str,
*,
violation: Literal[
"mutually_exclusive", "no_browser_misuse", "secret_stdin_misuse"
],
detected_auth_type: str | None = None,
)
Bases: ConfigError
Raised when a public API call combines mutually incompatible arguments.
Carries a violation discriminator and the resolved
detected_auth_type so non-CLI callers (Cowork's auth_manager.py,
JSON consumers) can dispatch programmatically without parsing the
human message. The CLI handle_errors decorator maps this subclass
to ExitCode.INVALID_ARGS (3) instead of the generic
GENERAL_ERROR (1) that ConfigError would otherwise produce.
Used by accounts.login_unified for the three documented
flag-combination rejections (043 contract, cli-commands.md §5):
--service-account + --token-env, --no-browser against a
non-browser auth type, and --secret-stdin against a non-SA
auth type.
Example
Initialize InvalidArgumentError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
violation
|
Discriminator for the kind of misuse. One of
TYPE:
|
detected_auth_type
|
The auth type the orchestrator resolved
from the supplied flags / env.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
OAuth Exceptions¶
Raised during OAuth 2.0 PKCE authentication flows and the mp login region probe.
| Error Code | Raised When |
|---|---|
OAUTH_TOKEN_ERROR |
Token exchange fails |
OAUTH_REFRESH_ERROR |
Token refresh fails (transient) |
OAUTH_REFRESH_REVOKED |
Refresh token rejected by IdP as invalid_grant (re-run mp login --name NAME) |
OAUTH_REGISTRATION_ERROR |
Dynamic client registration fails |
OAUTH_TIMEOUT |
Callback server times out waiting for authorization |
OAUTH_PORT_ERROR |
Cannot bind to a local port for the callback server |
OAUTH_BROWSER_ERROR |
Cannot open the authorization URL in the browser |
OAUTH_REGION_PROBE_FAILED |
mp login probed every region and none accepted the credential — see RegionProbeError below |
OAUTH_NETWORK_UNREACHABLE |
Every region probe failed at the network layer (DNS / TLS / connect refused) — see RegionProbeNetworkError below |
mixpanel_headless.OAuthError
¶
Bases: MixpanelHeadlessError
OAuth authentication flow error.
Raised for failures during the OAuth 2.0 PKCE flow, including token exchange, token refresh, client registration, callback timeout, port unavailability, and browser launch failures.
Error codes: - OAUTH_TOKEN_ERROR: Token exchange or validation failed - OAUTH_REFRESH_ERROR: Token refresh failed - OAUTH_REGISTRATION_ERROR: Dynamic Client Registration failed - OAUTH_TIMEOUT: Callback server timed out waiting for authorization - OAUTH_PORT_ERROR: All callback ports are occupied - OAUTH_BROWSER_ERROR: Could not open browser for authorization
Example
Initialize OAuthError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
code
|
Machine-readable error code. One of: OAUTH_TOKEN_ERROR, OAUTH_REFRESH_ERROR, OAUTH_REGISTRATION_ERROR, OAUTH_TIMEOUT, OAUTH_PORT_ERROR, OAUTH_BROWSER_ERROR.
TYPE:
|
details
|
Additional structured data about the error.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
RegionProbeError¶
Raised by mp login (and accounts.login_unified) when the us → eu → in region probe fails for every region. Subclass of OAuthError. The attempts attribute carries the full (region, status_code, error_body) list; status 0 indicates a network-layer failure (DNS / TLS / connect refused) — those cases raise RegionProbeNetworkError (subclass) so the CLI can render a different remediation hint.
import mixpanel_headless as mp
try:
mp.accounts.login_unified()
except mp.RegionProbeNetworkError as exc:
print("Could not reach any Mixpanel region. Check connectivity.")
for region, status, body in exc.attempts:
print(f" {region}: {body}")
except mp.RegionProbeError as exc:
print("Credential not valid in any region.")
for region, status, body in exc.attempts:
print(f" {region}: {status} {body}")
mixpanel_headless.RegionProbeError
¶
RegionProbeError(
message: str,
*,
attempts: list[tuple[Region, int, str]],
code: str = "OAUTH_REGION_PROBE_FAILED",
)
Bases: OAuthError
Raised when no region accepts the credential during region probing.
The region probe walks a configured order (default us → eu →
in) against /api/app/me, returning the first 200. When every
probe attempt fails, this exception is raised carrying the full
attempt list for diagnostic and telemetry use.
A status code of 0 indicates the request never reached the server
(network error); the third tuple element carries the failure detail
(HTTP response text or the network error reason).
See :class:RegionProbeNetworkError for the all-network-error
subclass — the probe distinguishes "credential rejected" from
"could not reach any region" so the CLI can render different
remediation hints.
Example
Initialize RegionProbeError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
attempts
|
Ordered list of
TYPE:
|
code
|
Machine-readable error code. Defaults to
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
attempts
property
¶
Ordered list of (region, status_code, error_body) tuples.
to_dict
¶
Serialize the exception to a JSON-friendly dict.
Includes attempts at the top level so consumers can inspect
the per-region probe outcomes without unpacking details.
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Any]
|
Dictionary with keys |
dict[str, Any]
|
|
dict[str, Any]
|
|
Source code in src/mixpanel_headless/exceptions.py
mixpanel_headless.RegionProbeNetworkError
¶
Bases: RegionProbeError
Raised when every region probe attempt failed at the network layer.
Subclass of :class:RegionProbeError used when ALL recorded
attempts have status_code == 0 — i.e. the credential was never
actually evaluated because no region was reachable (DNS failure,
TLS rejection, captive portal, no internet). The CLI catches this
before the generic RegionProbeError so it can render "could
not reach any Mixpanel region" instead of "credential not valid",
which would mislead a user who is actually offline.
Carries the same attempts shape as the parent so existing
consumers can render the per-region detail without changes.
Initialize RegionProbeNetworkError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
attempts
|
Ordered list of
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
Workspace / Organization Scope Exceptions¶
Raised when an auth-axis identifier (workspace or organization) cannot be resolved during App API requests.
| Error Code | Raised When |
|---|---|
NO_WORKSPACES |
No workspaces found for the project |
AMBIGUOUS_WORKSPACE |
Multiple workspaces found and none is marked as default |
WORKSPACE_NOT_FOUND |
Specified workspace ID does not exist |
ORGANIZATION_AMBIGUOUS |
An org-scoped business-context call could not auto-resolve the organization (active project absent from /me AND >1 accessible organization). details carries project_id and available_organizations. Pass organization_id=N explicitly to bypass auto-resolution. |
mixpanel_headless.WorkspaceScopeError
¶
WorkspaceScopeError(
message: str,
code: str = "NO_WORKSPACES",
details: dict[str, Any] | None = None,
)
Bases: MixpanelHeadlessError
Scope resolution error (workspace or organization).
Raised when an auth-axis identifier cannot be resolved during App
API requests. Originally introduced for workspace resolution; also
raised when the organization ID for an org-scoped business-context
call cannot be auto-derived from the cached /me response.
Error codes:
- NO_WORKSPACES: Project has no accessible workspaces
- AMBIGUOUS_WORKSPACE: Multiple workspaces, none default; must specify --workspace-id
- WORKSPACE_NOT_FOUND: Explicit workspace ID doesn't match any workspace
- ORGANIZATION_AMBIGUOUS: Cannot auto-resolve the organization for an
org-scoped call (active project absent from /me AND >1 accessible
organization). The details dict carries project_id and
available_organizations. Pass organization_id=N explicitly
to bypass auto-resolution.
Example
Initialize WorkspaceScopeError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
code
|
Machine-readable error code. One of: NO_WORKSPACES, AMBIGUOUS_WORKSPACE, WORKSPACE_NOT_FOUND, ORGANIZATION_AMBIGUOUS.
TYPE:
|
details
|
Additional structured data about the error.
TYPE:
|
Source code in src/mixpanel_headless/exceptions.py
Business Context Exceptions¶
Raised by Workspace.set_business_context() when content exceeds the 50,000-character cap. The check runs before the HTTP call, so callers fail fast and don't waste a round-trip; the server enforces the same limit and would otherwise return QueryError (HTTP 400). See the Business Context guide for usage.
| Error Code | Raised When |
|---|---|
BUSINESS_CONTEXT_TOO_LONG |
len(content) > BUSINESS_CONTEXT_MAX_CHARS (50,000) |
The details dict carries length (the actual content length) and max (the configured limit) for programmatic recovery.
mixpanel_headless.BusinessContextValidationError
¶
Bases: MixpanelHeadlessError
Business context content failed client-side validation.
Raised by Workspace.set_business_context() when the supplied
content exceeds BUSINESS_CONTEXT_MAX_CHARS (50,000 characters).
The check runs before the HTTP call so callers can fail fast and
avoid a wasted round-trip — the server enforces the same limit
server-side and would otherwise return QueryError (HTTP 400).
The details dict carries length (the actual content length)
and max (the configured limit) for programmatic recovery.
Example
Initialize BusinessContextValidationError.
| PARAMETER | DESCRIPTION |
|---|---|
message
|
Human-readable error message.
TYPE:
|
details
|
Additional structured data — typically
TYPE:
|