Skip to content

feat(sdk): surface API deprecations via a generic response interceptor#3806

Open
Lingala (lingala-composio) wants to merge 1 commit into
nextfrom
ao/composio-3/generic-deprecation-interceptor
Open

feat(sdk): surface API deprecations via a generic response interceptor#3806
Lingala (lingala-composio) wants to merge 1 commit into
nextfrom
ao/composio-3/generic-deprecation-interceptor

Conversation

@lingala-composio

@lingala-composio Lingala (lingala-composio) commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Ref: https://www.notion.so/composio/Deprecation-of-APIs-399f261a6dfe80968feec49ed84e14b8?source=copy_link

Replaces the endpoint-specific SEC-339 initiate() deprecation check with a single header-driven response interceptor in both the TypeScript and Python SDKs. Any endpoint deprecated server-side is now surfaced automatically — no new SDK release required.

The interceptor inspects standard deprecation-signalling headers on every response and warns once per operation:

  • Deprecation (RFC 9745) — presence gates the warning; the @<epoch> value is parsed into a date (the literal "true" is not required).
  • Sunset (RFC 8594) — optional removal date; drives escalated wording as the date approaches or passes.
  • Link; rel="successor-version" / rel="deprecation" (RFC 8288) — optional replacement-endpoint or migration-docs URL.

Warnings dedupe by HTTP method + normalized route template, so repeated calls (and calls with different path params) collapse to one warning. A header-less flow on an endpoint never suppresses a later deprecated flow on the same operation.

New client options (both SDKs)

  • disableDeprecationWarnings / disable_deprecation_warnings — silence the warnings.
  • onDeprecation / on_deprecation — structured { method, path, deprecatedAt, sunset, successor } callback for custom telemetry.

Safety

Never throws, never mutates the response body, swallows malformed headers.

Migration

connectedAccounts.initiate() no longer carries its own deprecation gate (the .withResponse() / with_raw_response dance and one-time module guard are removed) — it flows through the generic mechanism like every other endpoint. The typed retired-endpoint 400 error is unchanged.

Implementation

  • TS: new utils/deprecation.ts; wired via the generated client's fetch option in composio.ts; warns via the SDK logger.
  • Python: parsers + _warn_if_deprecated + a HttpClient._process_response override in composio/client/__init__.py; options threaded through sdk.py; emits warnings.warn(..., DeprecationWarning).

Tests

  • New deprecation unit suites in both SDKs cover: warns on Deprecation; silent when absent; dedupe (incl. different path params); @<epoch> parsed as a date and literal "true" still warns via presence; Sunset/Link read into the message; escalation wording; opt-out respected; onDeprecation fired with structured payload; never throws on garbage; and the header-less-then-deprecated same-operation case.
  • Migrated the existing initiate tests off the removed bespoke gate; removed the obsolete initiate deprecation header gate blocks.

Verification

  • TS: 942 tests pass, tsc --noEmit clean, eslint clean, prettier clean.
  • Python: new + connected-accounts + sdk tests pass; ruff check/ruff format clean; mypy clean on changed files. _process_response override confirmed call-compatible with the pinned base client.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 10, 2026 6:27pm

Request Review

@sparkle-code-security

Copy link
Copy Markdown
Review Summary · Total 0 · 🟤 Critical 0 · 🔴 High 0 · 🟠 Warning 4

python/composio/client/__init__.py

🟠 Meaningful Error Handling — No Silent Failures · Lines 444, 446 · 2 violations

Issue
Catches broad Exception on user-provided callback and only logs at debug level with a generic message. Error is swallowed silently from the application's perspective.

Recommended fix

Upgrade logging to at least info level and include more context: `self._logger.info("on_deprecation callback error",
extra={"error": str(error), "operation": operation})` or use structured logging to record the callback invocation
failed. Alternatively, catch specific exceptions (TypeError, AttributeError) rather than bare `Exception`.

Issue
Catches broad Exception while inspecting response headers and only logs at debug level with a generic message. Creates an undiagnosable failure mode when header parsing fails.

Recommended fix

Upgrade logging to at least info level and include the actual exception details: `self._logger.info("Failed to process
deprecation headers", extra={"error": str(error)})`. Alternatively, log only specific exception types (AttributeError,
KeyError) at debug level, and log unexpected exceptions (ValueError, RuntimeError) at warning level.

ts/packages/core/src/utils/deprecation.ts

🟠 Meaningful Error Handling — No Silent Failures · Lines 294, 298 · 2 violations

Issue
Empty catch block with only a comment. Broad exception catching with no recovery action, logging, or error transformation.

Recommended fix

Add explicit logging when the callback throws, even at a lower level. Replace the empty catch with: `catch (error) {
logger.debug('on_deprecation callback error', { error: error instanceof Error ? error.message : String(error) }); }` to
document the failure while maintaining traffic safety.

Issue
Empty catch block with only a comment. Broad exception catching that swallows all errors during deprecation inspection with no error logging or context.

Recommended fix

Add explicit debug logging to document what failed: `catch (error) { logger.debug('Failed to inspect response for
deprecation headers', { error: error instanceof Error ? error.message : String(error) }); }`. This preserves traffic
safety while enabling debugging.

Sparkle AI One-Click Remediation Use this prompt in your AI assistant or CLI to patch all findings in one pass.
Fix all 4 issues flagged by the Sparkle PR review below.
For each issue, update the specified file and line while preserving existing behavior and surrounding code style.

[Line 294] Meaningful Error Handling — No Silent Failures (warning)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block with only a comment. Broad exception catching with no recovery action, logging, or error
transformation.
Fix: Add explicit logging when the callback throws, even at a lower level. Replace the empty catch with: `catch (error)
{ logger.debug('on_deprecation callback error', { error: error instanceof Error ? error.message : String(error) }); }`
to document the failure while maintaining traffic safety.

[Line 298] Meaningful Error Handling — No Silent Failures (warning)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block with only a comment. Broad exception catching that swallows all errors during deprecation
inspection with no error logging or context.
Fix: Add explicit debug logging to document what failed: `catch (error) { logger.debug('Failed to inspect response for
deprecation headers', { error: error instanceof Error ? error.message : String(error) }); }`. This preserves traffic
safety while enabling debugging.

[Line 444] Meaningful Error Handling — No Silent Failures (warning)
File: python/composio/client/__init__.py
Issue: Catches broad `Exception` on user-provided callback and only logs at debug level with a generic message. Error is
swallowed silently from the application's perspective.
Fix: Upgrade logging to at least info level and include more context: `self._logger.info("on_deprecation callback
error", extra={"error": str(error), "operation": operation})` or use structured logging to record the callback
invocation failed. Alternatively, catch specific exceptions (TypeError, AttributeError) rather than bare `Exception`.

[Line 446] Meaningful Error Handling — No Silent Failures (warning)
File: python/composio/client/__init__.py
Issue: Catches broad `Exception` while inspecting response headers and only logs at debug level with a generic message.
Creates an undiagnosable failure mode when header parsing fails.
Fix: Upgrade logging to at least info level and include the actual exception details: `self._logger.info("Failed to
process deprecation headers", extra={"error": str(error)})`. Alternatively, log only specific exception types
(AttributeError, KeyError) at debug level, and log unexpected exceptions (ValueError, RuntimeError) at warning level.

Blast Radius Analysis No connected services identified. Changes appear isolated to the current repository.

@lingala-composio Lingala (lingala-composio) force-pushed the ao/composio-3/generic-deprecation-interceptor branch from 11dcb58 to 533c479 Compare July 10, 2026 18:18
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Security Audit Warning

The pnpm audit --prod check found security vulnerabilities in production dependencies.

Please review and fix the vulnerabilities. You can try running:

pnpm audit --fix --prod
Audit output
┌─────────────────────┬────────────────────────────────────────────────────────┐
│ low                 │ @ai-sdk/provider-utils has an Uncontrolled Resource    │
│                     │ Consumption issue                                      │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Package             │ @ai-sdk/provider-utils                                 │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Vulnerable versions │ <=3.0.97                                               │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Patched versions    │ >=3.0.98                                               │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ Paths               │ ts__e2e-tests__runtimes__node__mastra-tool-router-zod- │
│                     │ v3>@mastra/core>@ai-sdk/provider-utils                 │
│                     │                                                        │
│                     │ ts__e2e-tests__runtimes__node__mastra-tool-router-zod- │
│                     │ v3>@mastra/mcp>@mastra/core>@ai-sdk/provider-utils     │
│                     │                                                        │
│                     │ ts__e2e-tests__runtimes__node__mastra-tool-router-zod- │
│                     │ v4>@mastra/core>@ai-sdk/provider-utils                 │
│                     │                                                        │
│                     │ ... Found 14 paths, run `pnpm why                      │
│                     │ @ai-sdk/provider-utils` for more information           │
├─────────────────────┼────────────────────────────────────────────────────────┤
│ More info           │ https://github.com/advisories/GHSA-866g-f22w-33x8      │
└─────────────────────┴────────────────────────────────────────────────────────┘
1 vulnerabilities found
Severity: 1 low

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 533c479. Configure here.

self._on_deprecation = on_deprecation
# Operations we have already warned about, so a repeatedly-called
# deprecated operation only warns once. Keyed by ``METHOD path-template``.
self._warned_deprecated_operations: t.Set[str] = set()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clone drops deprecation options

Medium Severity

HttpClient.copy re-injects provider and _strict_response_validation for cloned clients, but not disable_deprecation_warnings or on_deprecation. The without_retries sibling (used by tools.execute and tools.proxy) is built via with_options(max_retries=0), so it keeps default warning behavior and loses the telemetry hook even when the parent client opted out or registered on_deprecation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 533c479. Configure here.

@sparkle-code-security

Copy link
Copy Markdown
Review Summary · Total 2 · 🟤 Critical 0 · 🔴 High 2

ts/packages/core/src/utils/deprecation.ts

🔴 Meaningful Error Handling — No Silent Failures · Lines 294, 298 · 2 violations

Issue
Empty catch block with only a comment. The catch statement catches a broad exception type but does not log or record any information about the failure.

Recommended fix

Add logging to the catch block: `catch (error) { logger.debug('on_deprecation callback raised; ignoring'); }`. The
defensive pattern of swallowing errors is acceptable at a boundary, but it must still emit a debug log statement to aid
troubleshooting.

Issue
Empty catch block with only a comment. The catch statement catches a broad exception type but performs no logging or error recording.

Recommended fix

Add logging to the catch block: `catch (error) { logger.debug('Failed to inspect response for deprecation headers'); }`.
This maintains the defensive pattern while recording that an inspection failure occurred.

Sparkle AI One-Click Remediation Use this prompt in your AI assistant or CLI to patch all findings in one pass.
Fix all 2 issues flagged by the Sparkle PR review below.
For each issue, update the specified file and line while preserving existing behavior and surrounding code style.

[Line 294] Meaningful Error Handling — No Silent Failures (high)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block with only a comment. The catch statement catches a broad exception type but does not log or
record any information about the failure.
Fix: Add logging to the catch block: `catch (error) { logger.debug('on_deprecation callback raised; ignoring'); }`. The
defensive pattern of swallowing errors is acceptable at a boundary, but it must still emit a debug log statement to aid
troubleshooting.

[Line 298] Meaningful Error Handling — No Silent Failures (high)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block with only a comment. The catch statement catches a broad exception type but performs no logging
or error recording.
Fix: Add logging to the catch block: `catch (error) { logger.debug('Failed to inspect response for deprecation
headers'); }`. This maintains the defensive pattern while recording that an inspection failure occurred.

Blast Radius Analysis No connected services identified. Changes appear isolated to the current repository.

Replace the endpoint-specific SEC-339 `initiate()` deprecation check with a
single header-driven response interceptor in both the TypeScript and Python
SDKs, so any endpoint deprecated server-side is surfaced automatically with no
new SDK release.

The interceptor inspects standard deprecation-signalling headers on every
response and warns once per operation:
  - `Deprecation` (RFC 9745) — presence gates the warning; the `@<epoch>`
    value is parsed into a date (literal "true" is not required).
  - `Sunset` (RFC 8594) — optional removal date; drives escalated wording as
    the date approaches or passes.
  - `Link; rel="successor-version"` / `rel="deprecation"` (RFC 8288) — optional
    replacement endpoint or migration-docs URL.

Warnings dedupe by HTTP method + normalized route template, so repeated calls
(and calls with different path params) collapse to one warning. Two new client
options are added to both SDKs: `disableDeprecationWarnings` /
`disable_deprecation_warnings` to silence warnings, and `onDeprecation` /
`on_deprecation` for a structured `{method, path, deprecatedAt, sunset,
successor}` callback for custom telemetry. The interceptor never throws, never
mutates the response, and swallows malformed headers.

`connectedAccounts.initiate()` no longer carries its own deprecation gate — it
now flows through the generic mechanism like every other endpoint. The typed
retired-endpoint 400 error is unchanged.

TS: routes through the generated client's `fetch` option and logs via the SDK
logger. Python: overrides `HttpClient._process_response` and emits a
`DeprecationWarning`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sparkle-code-security

Copy link
Copy Markdown
Review Summary · Total 0 · 🟤 Critical 0 · 🔴 High 0 · 🟠 Warning 3

ts/packages/core/src/utils/deprecation.ts

🟠 Meaningful Error Handling — No Silent Failures · Lines 233, 287, 298 · 3 violations

Issue
Empty catch block that silently swallows URL parsing exceptions without logging. The guardrail explicitly forbids empty catch blocks. While the code does recover with defined behavior (falling back to the raw URL), this violates the principle of explicit error handling and could mask unexpected failure modes.

Recommended fix

Add logging to the catch block: `catch (error) { logger.debug(`Failed to parse URL: ${error}`); }` to make the error
handling explicit and aid in debugging. Alternatively, add a non-empty statement like `void 0;` with an explanatory
comment if logging is undesirable at this boundary.

Issue
Empty catch block that silently swallows exceptions from the onDeprecation callback without logging. The guardrail requires that every caught exception either recovers with logging, re-throws with context, or is explicitly handled with a non-empty statement. This empty catch block hides potential issues in user-provided callbacks.

Recommended fix

Add logging: `catch (error) { logger.debug(`onDeprecation callback raised: ${error}`); }` to make failures in user code
visible for debugging, or add a comment with an explicit statement to acknowledge the intentional error suppression.

Issue
Empty catch block that silently swallows all exceptions from the deprecation inspection without logging. Even though this is at a boundary handler where robustness is critical, the guardrail forbids empty catch blocks. Exceptions in header inspection or other operations are completely hidden from diagnostic visibility.

Recommended fix

Add logging: `catch (error) { logger.debug(`Deprecation inspection failed: ${error}`); }` to ensure failures don't go
undetected. This provides visibility for debugging while still ensuring exceptions don't propagate and break API
traffic.

Sparkle AI One-Click Remediation Use this prompt in your AI assistant or CLI to patch all findings in one pass.
Fix all 3 issues flagged by the Sparkle PR review below.
For each issue, update the specified file and line while preserving existing behavior and surrounding code style.

[Line 233] Meaningful Error Handling — No Silent Failures (warning)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block that silently swallows URL parsing exceptions without logging. The guardrail explicitly forbids
empty catch blocks. While the code does recover with defined behavior (falling back to the raw URL), this violates the
principle of explicit error handling and could mask unexpected failure modes.
Fix: Add logging to the catch block: `catch (error) { logger.debug(`Failed to parse URL: ${error}`); }` to make the
error handling explicit and aid in debugging. Alternatively, add a non-empty statement like `void 0;` with an
explanatory comment if logging is undesirable at this boundary.

[Line 287] Meaningful Error Handling — No Silent Failures (warning)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block that silently swallows exceptions from the `onDeprecation` callback without logging. The
guardrail requires that every caught exception either recovers with logging, re-throws with context, or is explicitly
handled with a non-empty statement. This empty catch block hides potential issues in user-provided callbacks.
Fix: Add logging: `catch (error) { logger.debug(`onDeprecation callback raised: ${error}`); }` to make failures in user
code visible for debugging, or add a comment with an explicit statement to acknowledge the intentional error
suppression.

[Line 298] Meaningful Error Handling — No Silent Failures (warning)
File: ts/packages/core/src/utils/deprecation.ts
Issue: Empty catch block that silently swallows all exceptions from the deprecation inspection without logging. Even
though this is at a boundary handler where robustness is critical, the guardrail forbids empty catch blocks. Exceptions
in header inspection or other operations are completely hidden from diagnostic visibility.
Fix: Add logging: `catch (error) { logger.debug(`Deprecation inspection failed: ${error}`); }` to ensure failures don't
go undetected. This provides visibility for debugging while still ensuring exceptions don't propagate and break API
traffic.

Blast Radius Analysis No connected services identified. Changes appear isolated to the current repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant