Error Handling#

This guide covers practical error-handling patterns for salespyforce, including:

  • How API errors are currently surfaced by the library

  • How to add safe retries in your application code

  • How to collect useful diagnostics during failures

  • Gaps in current behavior that are good candidates for future releases

Before applying these patterns, make sure your client is configured correctly. See Authentication and Quickstart.

Error Types You Should Expect#

In current salespyforce releases, API failures can appear in a few forms:

  • RuntimeError from API helper methods when Salesforce returns HTTP status >= 300 (for example, get(), post(), patch(), put(), delete()).

  • requests exceptions (for example, connection or timeout errors) can bubble up directly in places where they are not wrapped.

  • Library-specific exceptions from salespyforce.errors.exceptions, such as:

    • InvalidURLError

    • MissingRequiredDataError

    • InvalidParameterError

    • DataMismatchError

    • APIRequestError (used in selected flows, such as retrieve_current_user_info(..., raise_exc_on_error=True))

For reference details, see Exception Classes and Client API.

Base Handling Pattern#

Use a layered try/except approach:

  • Catch library-specific exceptions first for validation and misuse cases.

  • Catch requests.RequestException next for transport/network failures.

  • Catch RuntimeError for HTTP error responses surfaced by current API methods.

  • Add a final broad fallback to ensure your application does not silently fail.

import logging
import requests

from salespyforce import Salesforce
from salespyforce.errors import exceptions as spf_exc

logger = logging.getLogger(__name__)

sfdc = Salesforce(helper="/path/to/helper.yml")

try:
    result = sfdc.soql_query("SELECT Id, Name FROM Account LIMIT 5")
except spf_exc.SalesPyForceError as exc:
    logger.error("SalesPyForce validation/domain error: %s", exc)
    raise
except requests.RequestException as exc:
    logger.error("Network/transport error calling Salesforce: %s", exc)
    raise
except RuntimeError as exc:
    logger.error("Salesforce API returned an error response: %s", exc)
    raise
except Exception as exc:
    logger.exception("Unexpected failure: %s", exc)
    raise

Retry Strategy#

salespyforce does not currently implement built-in retry/backoff behavior for API calls. In production workloads, implement retries in your application layer.

What To Retry#

Retry only transient failures, such as:

  • Transport exceptions (requests.ConnectionError, requests.Timeout)

  • HTTP 429 (rate limiting)

  • HTTP 5xx responses

Do not blindly retry all 4xx responses (many are permanent input/auth issues).

Idempotency Guidance#

  • Safest to retry: GET, read-only operations (soql_query, search_string).

  • Retry with caution: PATCH, PUT, DELETE (usually idempotent with same target/payload assumptions, but validate business impact).

  • Be careful with POST creates: retries can create duplicates unless you add deduplication or idempotency controls in your integration design.

Example Retry Wrapper (Exponential Backoff)#

import random
import time
import requests

from salespyforce import Salesforce


def call_with_retry(func, *, attempts=4, base_delay=0.5, max_delay=8.0):
    for attempt in range(1, attempts + 1):
        try:
            return func()
        except requests.RequestException:
            if attempt == attempts:
                raise
        except RuntimeError as exc:
            message = str(exc)
            transient = any(code in message for code in (" 429 ", " 500 ", " 502 ", " 503 ", " 504 "))
            if not transient or attempt == attempts:
                raise
        sleep_seconds = min(max_delay, base_delay * (2 ** (attempt - 1)))
        sleep_seconds += random.uniform(0, 0.2 * sleep_seconds)  # jitter
        time.sleep(sleep_seconds)


sfdc = Salesforce(helper="/path/to/helper.yml")
accounts = call_with_retry(
    lambda: sfdc.soql_query("SELECT Id, Name FROM Account ORDER BY LastModifiedDate DESC LIMIT 50")
)

Diagnostics And Observability#

When handling failures, collect enough context to debug quickly:

  • Operation metadata: method, endpoint, object type, record ID, query shape.

  • Timing metadata: timeout used, start/end timestamps, retry attempt number.

  • Salesforce context: org identifier, API version, user ID (if available).

  • Error data: exception class, message, and traceback.

Avoid Logging Secrets#

Do not log:

  • Access tokens

  • Passwords/security tokens

  • Connected app client secrets

  • Full request payloads that contain PII unless sanitized

Timeouts#

The API helper methods support a timeout parameter (default 30 seconds). Tune this per operation:

  • Lower timeout for frequent, interactive reads.

  • Higher timeout for large/slow operations where appropriate.

Example:

result = sfdc.get(
    endpoint=f"/services/data/{sfdc.version}/limits",
    timeout=15,
    show_full_error=False,
)

Practical Checklist#

  • Use targeted exception handling (SalesPyForceError, requests, RuntimeError).

  • Implement retry with exponential backoff + jitter for transient failures.

  • Restrict retries to operations that are safe in your business context.

  • Set explicit timeouts for long-running or latency-sensitive operations.

  • Log structured diagnostics, but redact credentials and sensitive payload data.

  • Alert on persistent 429 and 5xx patterns.