feat(cli): show authentication source on auth errors#321
Conversation
When a 401/403 error occurs, the CLI now prints a hint showing where the credentials came from (environment variable BL_API_KEY, credentials file, etc.) so that stale or mismatched credentials are immediately obvious. Adds: - cli/core/auth_source.go: ResolveAuthSource, IsAuthError, PrintAuthSourceHint - Wires auth source resolution into PersistentPreRunE - PrintError now appends auth source hint on auth failures Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
The hint now fires from ExitWithError (the universal exit point) so it covers error paths that use fmt.Printf directly (e.g. bl get models) rather than PrintError. A dedup flag ensures the hint is printed at most once even when both PrintError and ExitWithError are on the same path. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Addresses review feedback: '401'/'403' substring matching could false- positive on port numbers or resource IDs. Now matches '401 '/'403 ' (trailing space) which aligns with the SDK error format '401 Unauthorized'. Added tests for false-positive scenarios. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🔍 Interaction Flow DiagramHere's a sequence diagram showing how the new authentication source hint feature flows through the CLI components: sequenceDiagram
participant User
participant RootCmd as root.go<br/>PersistentPreRunE
participant AuthSrc as auth_source.go<br/>AuthSource module
participant API as Blaxel API
participant ErrHandler as sentry.go / utils.go<br/>Error Handlers
Note over RootCmd: Initialization Phase
User->>RootCmd: Execute CLI command
RootCmd->>RootCmd: Create blaxel client
RootCmd->>AuthSrc: ResolveAuthSource(workspace)
alt Config file has credentials
AuthSrc-->>AuthSrc: Check APIKey / AccessToken / ClientCredentials
else Env var fallback
AuthSrc-->>AuthSrc: Check BL_API_KEY / BL_CLIENT_CREDENTIALS
end
AuthSrc-->>RootCmd: AuthSource{Method, Origin}
RootCmd->>AuthSrc: SetAuthSource(source)
Note over API: Command Execution Phase
RootCmd->>API: Authenticated API call
API-->>ErrHandler: 401/403 error response
Note over ErrHandler: Error Handling Phase
ErrHandler->>AuthSrc: IsAuthError(err)
AuthSrc-->>ErrHandler: true
ErrHandler->>AuthSrc: PrintAuthSourceHint()
AuthSrc->>AuthSrc: Check dedup flag (authHintPrinted)
AuthSrc->>User: ⚠ "Authentication is using [METHOD] from [ORIGIN]"
alt Credentials from env var
AuthSrc->>User: "unset BL_API_KEY / BL_CLIENT_CREDENTIALS"
end
SummaryThe PR adds a credential-source tracking module (
Note Posted by PR Sequence Diagram · Tag @mendral-app with feedback. |
| return strings.Contains(msg, "401 ") || | ||
| strings.Contains(msg, "403 ") || | ||
| strings.Contains(msg, "unauthorized") || | ||
| strings.Contains(msg, "permission denied") |
There was a problem hiding this comment.
🟡 IsAuthError false-positive on OS-level "permission denied" filesystem errors
The IsAuthError function at cli/core/auth_source.go:85 matches strings.Contains(msg, "permission denied") to detect HTTP 403-style auth errors. However, this also matches Go's standard os.ErrPermission (whose .Error() is literally "permission denied"), causing filesystem permission errors to be misclassified as authentication failures.
When a user has credentials configured (so authSource is populated) and a filesystem operation fails with "permission denied" (e.g., can't read a config file or working directory), PrintError (cli/core/utils.go:378) and ExitWithError (cli/core/sentry.go:86) will print a misleading hint like "Authentication is using API key from environment variable BL_API_KEY" — which has nothing to do with the actual error. This could lead users to waste time investigating or rotating their API keys for what is actually a file permission problem.
Realistic code paths that could trigger this
Multiple call sites pass potentially filesystem-sourced errors to PrintError + ExitWithError:
cli/push.go:207:core.PrintError("Push", fmt.Errorf("failed to get current working directory: %w", err))cli/push.go:301:core.PrintError("Push", fmt.Errorf("failed to read .env.build file: %w", buildEnvErr))cli/serve.go:139:core.PrintError("Serve", err)afteros.Getwd()failure
All of these could produce errors containing "permission denied" from the OS.
Prompt for agents
The IsAuthError function in cli/core/auth_source.go matches the substring "permission denied" to detect HTTP 403-style authorization failures. However, this also matches Go OS-level permission errors (os.ErrPermission is literally "permission denied"), causing false positives for filesystem errors.
The intended match is the auth error produced at cli/auth/utils.go:76 which returns fmt.Errorf("permission denied for workspace %q", workspace). Consider tightening the match to something more specific like "permission denied for workspace" or checking for os.ErrPermission / os.PathError with errors.Is/errors.As and excluding those before the string match. Alternatively, the auth/utils.go error message could be changed to use a more distinctive phrase like "access denied" that won't collide with POSIX permission errors.
Was this helpful? React with 👍 or 👎 to provide feedback.
| // ResolveAuthSource determines which credentials the CLI is actually using. | ||
| // | ||
| // Resolution order (mirrors the SDK): | ||
| // 1. Config-file credentials for the workspace (~/.blaxel/config.yaml) override | ||
| // environment variables because NewClientFromConfig appends them after | ||
| // DefaultClientOptions. | ||
| // 2. If no config-file credentials exist for the workspace, environment | ||
| // variables BL_API_KEY / BL_CLIENT_CREDENTIALS are used. |
There was a problem hiding this comment.
🚩 ResolveAuthSource resolution order claims to mirror SDK — not independently verifiable
The comment at cli/core/auth_source.go:37-43 states that config-file credentials override environment variables because NewClientFromConfig appends them after DefaultClientOptions ("last wins"). This resolution order is critical for the auth hint to be accurate — if the SDK actually uses env vars over config (or vice versa), the hint would mislead users about which credentials are active. The SDK source (github.com/blaxel-ai/sdk-go v0.18.0) isn't available locally to verify. If the precedence is wrong, users would get an incorrect hint (e.g., told their API key env var is being used when it's actually the config file credential, or vice versa).
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
There was a problem hiding this comment.
LGTM
Previous feedback was addressed: IsAuthError now uses trailing-space matching ("401 " / "403 ") and includes tests for false-positive scenarios. The implementation is clean and well-structured for a CLI diagnostic feature.
Tag @mendral-app with feedback or questions. View session
Summary
When a CLI command hits a 401/403, the user now sees where the credentials came from — making stale or mismatched credential mistakes immediately obvious (context: a user had a stale
BL_API_KEYenv var for the wrong workspace and didn't notice the CLI was using it).Works for all auth modes (API key, access token, client credentials) and all error paths (including
bl getwhich bypassesPrintError).Before:
After:
Or when an env var is in play:
How it works
cli/core/auth_source.go—ResolveAuthSource(workspace)mirrors the SDK credential resolution: config-file creds win over env vars; if no config creds exist, env vars are used.IsAuthError(err)detects 401/403 via SDK type or string matching.ExitWithError(the universal exit point) callsPrintAuthSourceHint()on auth errors — this covers paths likebl getthat usefmt.Printfdirectly.PrintErroralso calls the hint for paths that go through it.authHintPrintedflag deduplicates when both are on the same path.Link to Devin session: https://app.devin.ai/sessions/aea94031def046a2a32308b4534c250f
Requested by: @drappier-charles
Note
Adds authentication source hints to CLI error output on 401/403 failures, showing users whether credentials came from environment variables or config files. The latest commit addresses the previous review feedback about false-positive substring matching.
Written by Mendral for commit 035a11a.