Skip to content

fix: keep API keys out of unhandled CLI errors and SDK error messages - #548

Open
cdeil wants to merge 1 commit into
roboflow:mainfrom
cdeil:redact-api-key-errors
Open

cdeil wants to merge 1 commit into
roboflow:mainfrom
cdeil:redact-api-key-errors

Conversation

@cdeil

@cdeil cdeil commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to 183d0d4, which redacts api_key=... in output_error(). Three paths still print the key, because most requests pass it as a URL query parameter and requests puts the full URL into its exception messages:

  1. Unhandled exceptions in the CLI, for example a requests.ConnectionError from rfapi.get_project: in --json mode main() prints str(exc), in text mode Python prints the traceback.
  2. RoboflowError, ImageUploadError and AnnotationSaveError messages that wrap requests errors (for example ImageUploadError(str(e)) in rfapi.upload_image), which Workspace.upload_dataset() prints per image.
  3. The error lines that Version.deploy() and Workspace.deploy_model() print for failed model uploads.

Example on main:

$ API_URL=http://127.0.0.1:9 roboflow --json -k DUMMY-KEY -w ws project get proj
{"error": {"message": "HTTPConnectionPool(host='127.0.0.1', port=9): Max retries exceeded with url: /ws/proj?api_key=DUMMY-KEY (Caused by NewConnectionError(...))"}}

With this PR the message contains api_key=***. Text mode prints the same traceback as before, with the key replaced, and still exits with code 1.

Changes:

  • roboflow/util/redact.py: redact_api_key(), the pattern from cli/_output._sanitize_credentials(), which now delegates to it.
  • roboflow/cli/__init__.py: main() redacts unhandled errors in JSON mode and prints a redacted traceback in text mode.
  • roboflow/adapters/rfapi.py: RoboflowError and its subclasses redact their message.
  • roboflow/core/version.py, roboflow/core/workspace.py: the printed upload errors are redacted.

Proposal: send the key as a header instead

Redaction only treats the symptom. A URL with the key also ends up in server and proxy access logs, and raw requests exceptions raised from SDK functions still contain it for SDK users. The API accepts Authorization: Bearer <api_key>, and the SDK already uses it in vision_events_api.py, workflowevalsapi.py and the batch-processing helpers. In a read-only check, every read endpoint I tried accepted the header in place of ?api_key= (workspace, project, version, batches, workspace search). I could not check write endpoints (image upload, annotation save, model upload, version generation) or the inference hosts without side effects.

If you can confirm that all endpoints accept the header, the SDK could send it everywhere and stop putting the key into URLs; with a shared requests.Session (#491) that would be a change in one place. I'm happy to help with that.

Test plan

  • tests/util/test_redact.py: the helper, the three exception classes, and the printed Version.deploy() error.
  • tests/cli/test_redact_errors.py: main() in JSON and in text mode with a mocked requests.ConnectionError. Both tests fail on main and pass with this change.
  • Python 3.13: make check_code_quality passes; python -m unittest runs 1216 tests, OK (1 skipped). The slim job (tests.test_slim_compat, tests.test_vision_events) passes on Python 3.10.

cc @yeldarby @iurisilvio

The CLI already redacts api_key=... in output_error(), but unhandled
exceptions bypass it: in --json mode main() prints str(exc), and in text
mode Python prints the traceback. For requests errors (connection
failures, raise_for_status) both contain the request URL with the key.

Move the pattern into roboflow.util.redact.redact_api_key() and apply it
in main() for both modes, in RoboflowError and its subclasses (which
wrap requests errors, for example in image uploads), and in the error
lines the SDK prints for failed model and image uploads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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