Skip to content

Commit 246e6a6

Browse files
yeldarbyclaude
andcommitted
fix(cli): auto-detect plan-gated errors, add upgrade hints, fix deployment crash
1. Add _detect_plan_hint() that automatically appends upgrade/pricing hints when error messages contain plan-related keywords (Growth plan, Enterprise, folder billing, Unauthorized, over_quota). Applied to ALL errors through output_error() when no explicit hint is provided. 2. Fix deployment handler wrapper to catch ConnectionError (not just SystemExit) — prevents raw Python tracebacks when the deployment service is unreachable. 3. Previously committed: _sanitize_credentials strips API keys from error messages. 405 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 183d0d4 commit 246e6a6

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

roboflow/cli/_output.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,25 @@ def output(args: Any, data: Any, text: Optional[str] = None) -> None:
3636
print(json.dumps(data, indent=2, default=str))
3737

3838

39+
_PLAN_HINT_PATTERNS: list[tuple[str, str]] = [
40+
("require", "This feature requires a higher plan. Visit https://roboflow.com/pricing to upgrade."),
41+
("Growth plan", "This feature requires a Growth plan or higher. Visit https://roboflow.com/pricing to upgrade."),
42+
("Enterprise", "This feature requires an Enterprise plan. Contact sales@roboflow.com to upgrade."),
43+
("folder billing", "This feature requires folder billing. Visit https://app.roboflow.com/settings to enable it."),
44+
("Unauthorized", "Check your API key and workspace permissions. Some features require specific plan tiers."),
45+
("over_quota", "Your workspace has exceeded its quota. Visit https://roboflow.com/pricing to upgrade."),
46+
]
47+
48+
49+
def _detect_plan_hint(message: str) -> Optional[str]:
50+
"""Detect plan/billing-related errors and return an appropriate upgrade hint."""
51+
lower = message.lower()
52+
for pattern, hint in _PLAN_HINT_PATTERNS:
53+
if pattern.lower() in lower:
54+
return hint
55+
return None
56+
57+
3958
def _sanitize_credentials(text: str) -> str:
4059
"""Strip API keys from URLs and other sensitive patterns in error messages."""
4160
import re
@@ -91,6 +110,10 @@ def output_error(
91110
"""
92111
parsed, human_message = _parse_error_message(message)
93112

113+
# Auto-detect plan-gated errors and add upgrade hints when none provided
114+
if not hint:
115+
hint = _detect_plan_hint(human_message)
116+
94117
if getattr(args, "json", False):
95118
# Normalise error to always be {"error": {"message": "..."}} so
96119
# consumers see a consistent schema regardless of error source.

roboflow/cli/handlers/deployment.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,22 @@ def _wrapped(args: argparse.Namespace) -> None:
3232
except SystemExit as exc:
3333
sys.stdout = orig_stdout
3434
code = exc.code if isinstance(exc.code, int) else 1
35-
# Map legacy exit codes to CLI conventions: 1=general, 2=auth, 3=not-found
3635
exit_code = {0: 1, 1: 1, 2: 2, 3: 3}.get(code, 1) if code else 1
3736
text = captured.getvalue().strip()
3837
if text:
3938
output_error(args, text, exit_code=exit_code)
4039
else:
4140
output_error(args, "Deployment command failed.", exit_code=1)
4241
return
42+
except Exception as exc:
43+
sys.stdout = orig_stdout
44+
output_error(
45+
args,
46+
f"Deployment service unavailable: {type(exc).__name__}",
47+
hint="The dedicated deployment service may be down or unreachable. Try again later.",
48+
exit_code=1,
49+
)
50+
return
4351
finally:
4452
sys.stdout = orig_stdout
4553

0 commit comments

Comments
 (0)