Skip to content

feat: ask for an AWS profile and region when the default credentials fail - #967

Merged
cchu-ld merged 4 commits into
mainfrom
cchu/aws-profile-picker
Oct 26, 2026
Merged

cchu-ld merged 4 commits into
mainfrom
cchu/aws-profile-picker

Conversation

@cchu-ld

@cchu-ld cchu-ld commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Describe the solution you've provided
With this PR, if no profile is selected and the default credentials do not work, setup and status ask which profile to use:

Screenshot 2026-10-23 at 5 44 37 PM
  • The picker reads the profiles from ~/.aws/config and ~/.aws/credentials. It shows the account, region, and role of each profile. It reads only those keys, so a secret access key never leaves the file.
  • A profile with a valid SSO token in ~/.aws/sso/cache has a ✓ and comes first. All the profiles that share an SSO session share one token.
  • The picker shows all the rows of a list with 10 rows or fewer. In a longer list, the highlight moves down to the last row, and then the list scrolls under it, so the picker never takes more than 10 rows. Type to search by substring, ignoring case. The search matches any part of the line, for example an account ID or a role. Esc clears the search, and a second Esc or Ctrl+C closes the picker.
  • After the choice, the command prints Using AWS profile X. To skip this question, pass --profile X or run export AWS_PROFILE=X.
  • If the profile has no region, the command asks for one of the supported regions in the same way.
  • If the selected profile has an expired SSO token, the current aws sso login offer runs after the choice.
  • If the default profile uses SSO and its token is expired, the command offers aws sso login --profile default and does not open the picker. This keeps the run in the account of the default profile. If the user answers no, the command returns the error, which names --profile. It does not go back to the picker.
  • If the default profile has no region, the command tries the default credentials again with a supported region. If they do not work, the command asks for a profile. If they work, it asks for a region.
  • Without a terminal, the command does not ask and returns the current error. This is also true with --yes.
  • With --yes in a terminal, the command uses the one signed-in profile if there is exactly one, and prints Using AWS profile X. It asks for no confirmation. Before this PR, the same run returned the error. Otherwise it returns the current error.
  • A working default profile, --profile, AWS_PROFILE, or AWS_ACCESS_KEY_ID skips the picker.

The agent space question in setup now uses the same picker, in place of the numbered list. If a space matches --agent-space-name, or if setup made exactly one space, the highlight starts on that space. If the choice is not clear, the highlight starts on "provision a new agent space", so Enter does not connect to a space of another owner.

The aws sso login question and the setup Continue? question now use [Y/n], so Enter accepts. If stdin closes with no answer, the result is a no.

The picker is a small bubbletea model with a bubbles text input. It runs inline, not in the alternate screen, so the output above it stays visible. It cuts each line to the terminal width, because a wrapped line breaks the redraw. It uses the accent color of the sync pickers, and it gets its color support from stderr, where it draws. The huh select was not usable here: in huh v1.0.0, Select.Update sets the window offset to the highlighted row on every key, so the highlight stayed on the first row.

Describe alternatives you've considered

#962 improves the error messages and does not ask. It lists five profiles with account and role, and tells the user to pass --profile. The two PRs can merge together. They both change NewClients errors and loadClients, so the second one to merge needs a small rebase.

Additional context

A ✓ uses only the access token in the SSO cache. A session with an expired access token and a refresh token shows no ✓, but the SDK can still refresh it. This changes only the sort order and the --yes count.

The command is hidden, so users do not see this yet.

To try it, unset AWS_PROFILE and AWS_REGION, make sure that no default profile works, and run ./ldcli aws-devops-agent status.

🤖 Generated with Claude Code


Note

Overview
When default AWS credentials fail, setup and status now walk users through credentials interactively instead of failing immediately. A searchable terminal picker (bubbletea) lists profiles from shared AWS config—SSO-signed-in profiles first with a ✓—and can prompt for a supported region if none is set. The chosen profile is written back to --profile, with hints to skip future prompts. Expired default SSO still triggers aws sso login rather than switching accounts; --yes auto-picks when exactly one SSO profile is signed in.

The same picker replaces numbered prompts for agent space selection during setup. Highlight defaults are safer: ambiguous cases start on “provision a new agent space” so Enter won’t attach to someone else’s space. Continue? and SSO login prompts flip to [Y/n] (Enter = yes) via shared readYes.

New internal/awsdevops/profiles parses config/credentials for display metadata only (no secret keys) and checks SSO cache expiry.

Reviewed by Cursor Bugbot for commit 963dfc0. Bugbot is set up for automated code reviews on this repo. Configure here.

cchu-ld and others added 2 commits October 23, 2026 17:29
…fail

If no profile is selected and the default credentials do not work, setup
and status ask which profile to use. The picker lists the profiles with
an SSO session first, with the account, region, and role of each. It
shows 10 profiles at most and scrolls in place. Press / to search.

If the profile has no region, the command asks for one.

Without a terminal, the command does not ask. With --yes, it uses the
one signed-in profile.

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

The huh select moved its window to the highlighted row on every key, so
the highlight stayed on the first row and the rows above it were hidden.

The new picker shows all rows of a list with 10 rows or fewer. In a longer
list, the highlight moves down to the last row, and then the list scrolls
under it. Typing searches at once, and Esc clears the search. The title
and the highlighted row use the accent color of the sync pickers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cchu-ld
cchu-ld marked this pull request as ready for review October 24, 2026 00:48
@cchu-ld
cchu-ld requested a review from a team as a code owner October 24, 2026 00:48
@cchu-ld
cchu-ld requested review from a team and nieblara and removed request for a team October 24, 2026 00:48

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit af70fbd. Configure here.

Comment thread cmd/aws_devops_agent/clients.go Resolved

@Andrewjeska Andrewjeska 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.

nits:
1)

I dont know if it's expected or not, but when I'm prompted y/N, my default is to hit "enter" without thinking because I assume that it will "do the thing".

I think the default should be yes, take a look at npm package init to see what I mean

Image

For consistency, can we make this selectable like the profiles are?

Overall this is really great work!!

@Andrewjeska Andrewjeska 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.

A few suggestions, nothing blocking — nice PR.

cmd/aws_devops_agent/clients.go:117-127 — on ErrNoRegion the region-pick path re-runs newClients once with the picked region, and if that returns ErrNoCredentials control falls through into the SSO-login block below. The logic is right but takes a minute to trace. A one-line comment on clientsAfter making the fall-through intent explicit would help.

cmd/aws_devops_agent/clients.go:74 — the errors.Is(err, awsdevops.ErrNoRegion) && !awsdevops.HasProfile("default") disjunct is dense. The reason (no default profile → SDK's region error is really "no profile at all") is above the branch, but pulling it into a named helper like noProfileReason(err) would let the branch read in one line.

internal/awsdevops/profiles.go:868 (DefaultFallback) — only checks AWS_ACCESS_KEY_ID, so it returns true when the user is actually authenticated via AWS_SESSION_TOKEN-only creds, web-identity, or container/EC2 metadata. In those cases newClients succeeds and we skip the picker, so it works — but by accident. A short comment that this is intentional and relies on newClients not returning ErrNoCredentials for ambient creds would help a future reader.

cmd/aws_devops_agent/picker.go:239-258 (profileLabels) — len(p.Name) / len(p.Account) / len(p.Region) are byte lengths. A profile name with multibyte chars would misalign the columns. ansi.StringWidth is already imported in pickermodel.go and would fix it.
Minor:- pickermodel.go:788 — filter() lowercatroke. Fine for today's sizes;pre-lowercasing in newPicker would scale a larger list.

  • load() ends up parsing ~/.aws/config up to 3× (DefaultFallback → HasProfile → Profiles). Negligible unless the file is huge.
  • Picker renders to cmd.ErrOrStderr() but the Using AWS profile X… confirmation goes to l.out. Intentional?

via LD Research 🤖

cchu-ld and others added 2 commits October 26, 2026 10:59
…e the picker for the agent space

If the default profile has no region, ldcli now tries the default
credentials again with a supported region. If they do not work, ldcli asks
for a profile, not for a region.

The agent space list now uses the picker of the profile and region
questions. The highlight starts on the row that was the default.

The aws sso login question and the setup confirmation now use [Y/n], so
Enter accepts. If the input ends with no answer, the result is a no.

The profile labels now measure columns in terminal width, so a wide
character does not move the columns. The picker makes the lowercase labels
one time, not on each key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er did not name

If the agent space is not clear, the highlight now starts on "provision a
new agent space", so Enter does not connect to a space of another owner.

If the default profile uses SSO and its token is expired, ldcli now offers
aws sso login for that profile and does not open the profile picker.

With --yes, ldcli picks the one signed-in profile only in a terminal.

The picker highlight now gets its color support from the picker output,
not from stdout.

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

cchu-ld commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

@Andrewjeska Thanks for the review! Fixed in b9eb9519

  • The aws sso login question and the setup "Continue?" question now use [Y/n], so Enter accepts. If stdin closes with no answer, the result is still a no.
  • The agent space list now uses the same picker as the profiles and regions.
  • clientsAfter: I added a comment that names the fall-through from the region step to the SSO login step.
  • The dense noProfile check: the new defaultCredentialsFail helper replaces it. It also fixes the Bugbot issue.
  • DefaultFallback: I added a comment that says that the check of only AWS_ACCESS_KEY_ID is intentional, and why other ambient credentials still work.
  • profileLabels: the columns now use ansi.StringWidth, and a new test covers wide characters.
  • filter(): newPicker now makes the lowercase labels one time.
  • Parsing ~/.aws/config 3 times: HasProfile is gone, so the file gets one less read.
  • Picker output and the "Using AWS profile" line: these go to the same stream, because l.out is cmd.ErrOrStderr(). It is intentional.

@cchu-ld
cchu-ld merged commit 1ecd2fe into main Oct 26, 2026
11 checks passed
@cchu-ld
cchu-ld deleted the cchu/aws-profile-picker branch October 26, 2026 19:13
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.

2 participants