Show a one-line error for an invalid CLOUDINARY_URL - #122
Merged
Merged
Conversation
With `CLOUDINARY_URL=garbage`, every command printed a Python traceback. The SDK reads the variable when it is imported, before `main()` can handle errors. Catch the `ValueError` from `import cloudinary` and exit with one line that tells the user to fix or unset the variable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TalLevAmi
marked this pull request as ready for review
September 26, 2026 16:15
const-cloudinary
requested changes
Sep 27, 2026
const-cloudinary
left a comment
Member
There was a problem hiding this comment.
@TalLevAmi , this is a nice bug.
Unfortunately, the fix is not quite complete.
We have a few ways to configure CLI, using cld -C or cld -c. They take precedence over ENV variable, so we should not crash in this case, even if ENV variable is broken.
And this PR does not address CLOUDINARY_ACCOUNT_URL, which produces similar error.
…ary-url-error # Conflicts: # test/test_cli.py
The SDK reads both variables on import and raises ValueError when one is invalid. The CLI now imports the SDK without them and then loads each one again. An invalid variable stays unset, so -c and -C can select a config. Without -c or -C, the CLI shows a one-line error that names the variable.
const-cloudinary
requested changes
Sep 30, 2026
const-cloudinary
left a comment
Member
There was a problem hiding this comment.
@TalLevAmi thanks, the direction is right: the SDK raises during import, so the variables have to be held back around import cloudinary. Two things decide whether the environment is used at all, and the current version doesn't account for them yet:
Background
- The CLI has a saved default config. Users save named configs with
cld config -n <name> <url>and pick one as the default withcld config -d <name>, or with--set-defaultwhen saving. The choice is stored as__default__in~/.cloudinary-cli/config.json(defaults.py:29).resolve_cli_configchecks sources in this order:-c, then-C, then the saved default, then the environment (config_resolver.py:57, 63, 71, 81). With a default set,CLOUDINARY_URLis never read, so an invalid value in it shouldn't matter. - The SDK can be configured with separate variables, not only
CLOUDINARY_URL. WhenCLOUDINARY_CLOUD_NAMEis set and not empty, the SDK loads everyCLOUDINARY_*variable exceptCLOUDINARY_URL(CLOUDINARY_API_KEY,CLOUDINARY_API_SECRET, and so on) and ignoresCLOUDINARY_URLcompletely (pycloudinarycloudinary/__init__.py:205-218). SoCLOUDINARY_URL=garbagetogether withCLOUDINARY_CLOUD_NAME=demois a valid setup and should keep working. IfCLOUDINARY_CLOUD_NAMEis missing or empty, the SDK parses the URL, and it can fail even when other variables such asCLOUDINARY_API_KEYare set.
What this means for the PR
- Precedence. The check at
config_resolver.py:50runs before the saved default is looked at, so with a default set and a badCLOUDINARY_URLevery command exits. The error should only fire whereresolve_cli_configfalls back to the environment (config_resolver.py:81). config_optionalcommands.cld config -n <name> <url>is blocked too, and that's how a user would fix the problem. Look at howwarn_if_unconfiguredis handled (cli_group.py:47).CLOUDINARY_ACCOUNT_URLis separate. Onlycld provisioninguses it, and-c,-Cand the saved default don't replace it. The SDK always parses it, whatever else is set (pycloudinarycloudinary/provisioning/account_config.py:26-28). Right now a bad value stopsadmin ping, and with-cit's silently dropped. Keep a separate error for each variable and check this one inprovisioning.- Catching only
ValueErrormisses cases (__init__.py:21). TryCLOUDINARY_URL='cloudinary://1:s@demo?a=1&a[b]=2', which raises aTypeError. A failed load can also leave a half-filled config. - Deleting from
os.environ(__init__.py:22). Instead of deleting the variable, consider wrapping_load_config_from_envon the SDK config classes once, after import. Then every laterConfig()call is safe, the environment stays as it was, and the resolver can ask for the error when it needs it. - Structure. Right now this logic sits in
cloudinary_cli/__init__.py. It might be easier to read and test in a small helper underutils/. The only constraint is that the helper can't import the SDK at the top, because it has to run beforeimport cloudinary.cloudinary_cli/__init__.pywould then just call it. - Tests. The subprocess tests read the developer's real
~/.cloudinary-cli/config.json. SetCLOUDINARY_HOMEto a temp directory, and add cases for the saved default,-C,CLOUDINARY_CLOUD_NAMEset together with a bad URL, and the query-key URL.
Move the SDK import into the new helper `utils/env_config.py`. The helper holds back CLOUDINARY_URL and CLOUDINARY_ACCOUNT_URL during the import. After the import, it wraps `_load_config_from_env` of the SDK config classes. It does not delete the variables from the environment. - The wrapper catches ValueError and TypeError. A failed load keeps no values from the failed load. - The CLOUDINARY_URL error shows only when `resolve_cli_config` falls back to the environment. A saved default, -c and -C override it, and the commands that work without a config (such as `config -n`) ignore it. - The CLOUDINARY_ACCOUNT_URL error shows only in `provisioning`. - The subprocess tests use a temporary CLOUDINARY_HOME.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With an invalid
CLOUDINARY_URL(for exampleCLOUDINARY_URL=garbage), everycldcommand printed a full Python traceback that ended inValueError: Invalid CLOUDINARY_URL scheme. The SDK reads the variable when it is imported, so the error occurs before the error handling inmain().Now the output is:
Brief Summary of Changes
cloudinary_cli/__init__.pycatchesValueErrorfromimport cloudinaryand callssys.exitwith the message. The exit code is 1.What does this PR address?
Are tests included?
Reviewer, please note:
CLOUDINARY_URLis invalid and you select a saved config with-C. This is the same as before, because the SDK cannot be imported with that value.Checklist:
Local test run: I ran the full suite on Python 3.8 with Click 8.1.8. The new tests pass, and they fail without the fix. 8 tests in
test_cli_agent.pyandtest_cli_config_oauth.pyfail withstderr not separately capturedon this branch and onmastertoo. These tests need Click 8.2, which needs Python 3.10 or later. CI must confirm the full result.🤖 Generated with Claude Code