Skip to content

fix(cartesia): redact API keys from websocket handshake errors - #6740

Merged
chenghao-mou merged 3 commits into
livekit:mainfrom
LHMQ878:fix/6739-redact-cartesia-api-key
Aug 9, 2026
Merged

fix(cartesia): redact API keys from websocket handshake errors#6740
chenghao-mou merged 3 commits into
livekit:mainfrom
LHMQ878:fix/6739-redact-cartesia-api-key

Conversation

@LHMQ878

@LHMQ878 LHMQ878 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Wrap Cartesia TTS/STT websocket handshake failures as APIStatusError/APIConnectionError without chaining the aiohttp cause, so X-API-Key in RequestInfo cannot appear in exception/Task repr logs.
  • Make ConnectionPool.prewarm() swallow connect failures and log with %s (str) instead of leaving an unretrieved task exception (the path that published Cartesia API keys).

Test plan

  • pytest tests/test_connection_pool.py tests/test_plugin_cartesia_tts.py -v --unit --plugin cartesia
  • Confirm a failed Cartesia TTS prewarm no longer logs X-API-Key / raw API key material

Fixes #6739

aiohttp WSServerHandshakeError embeds X-API-Key in RequestInfo.repr, which
leaked via ConnectionPool.prewarm's unretrieved task logs. Wrap handshake
failures without chaining the aiohttp cause, and swallow prewarm errors
safely.

Fixes livekit#6739
@LHMQ878
LHMQ878 requested a review from a team as a code owner August 7, 2026 09:46
devin-ai-integration[bot]

This comment was marked as resolved.

# Swallow the error so asyncio does not log an unretrieved task
# exception. Use %s (str) rather than %r: aiohttp ClientResponseError
# embeds request headers (including API keys) in its repr.
logger.warning("failed to prewarm connection pool: %s", e)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this could leak url credentials as well such as ?api_key=... or &jwt_token=...

we can do something like this:

logger.warning(
            "failed to prewarm connection pool",
            extra={"exception_type": type(e).__name__},
        )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — thanks. I updated the prewarm failure log to only record the exception type via extra, so URL credentials like ?api_key= / &jwt_token= no longer show up in the warning message.

…warm logs

Log only exception types on prewarm failure, and break __cause__ chains on
generic Cartesia connect errors so auth headers and URL credentials cannot
surface in logs.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment thread tests/test_plugin_cartesia_tts.py Outdated
Comment on lines +62 to +64
leaky = ConnectionError(
f"wss://api.cartesia.ai/tts/websocket?api_key={SECRET_API_KEY}"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 New test code is not formatted, so the automated formatting check fails

Two newly added multi-line call expressions are written split across lines even though they fit within the 100-character limit (ConnectionError(...) at tests/test_plugin_cartesia_tts.py:62-64), so the repository's mandatory formatting check reports a diff and rejects the change.
Impact: The project's required format check fails on these files, blocking the automated quality gate.

Why ruff format collapses these calls

CONTRIBUTING.md requires running ruff format before committing and AGENTS.md requires make check (which includes format-check). Both new call sites have no magic trailing comma and fit on one line once joined:

  • tests/test_plugin_cartesia_tts.py:62-64 joins to 92 characters.
  • tests/test_connection_pool.py:132-134 joins to 98 characters.

ruff format therefore reformats both onto a single line, so make format-check fails.

Suggested change
leaky = ConnectionError(
f"wss://api.cartesia.ai/tts/websocket?api_key={SECRET_API_KEY}"
)
leaky = ConnectionError(f"wss://api.cartesia.ai/tts/websocket?api_key={SECRET_API_KEY}")
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chenghao-mou
chenghao-mou merged commit 6630de0 into livekit:main Aug 9, 2026
15 checks passed
AALG123 added a commit to AALG123/agents that referenced this pull request Aug 10, 2026
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.

never retrieved exception=WSServerHandshakeError leads to published Cartesia API Key in logs

2 participants