Skip to content

fix: error on CLI HTTP redirects instead of following them - #29104

Merged
f0ssel merged 4 commits into
mainfrom
fix/cli-reject-redirects
Sep 9, 2026
Merged

f0ssel merged 4 commits into
mainfrom
fix/cli-reject-redirects

Conversation

@f0ssel

@f0ssel f0ssel commented Sep 8, 2026

Copy link
Copy Markdown
Member

Problem

codersdk.Client uses an http.Client with no CheckRedirect, so Go's default applies: a 301/302/303 is followed by downgrading the request to a GET with no body. When ~/.config/coderv2/url points at a stale host that redirects to the new deployment, coder tokens create silently becomes a GET /api/v2/users/{user}/keys/tokens (the list endpoint, same path) and fails with:

error: create tokens: json: cannot unmarshal array into Go value of type codersdk.GenerateAPIKeyResponse

Fix

Set CheckRedirect on the HTTP client built in cli/root.go (createHTTPClient, used by every CLI command including coder login) to refuse redirects and return a typed redirectError. The CLI error formatter prints the original and target URLs plus a suggestion to run coder login <new URL> when the redirect changes host or scheme, or a proxy/path-rewrite hint when it stays within the same deployment.

Encountered an error running "coder tokens create", see "coder tokens create --help" for more information
error: Trace=[get token config: Get "https://dogfood.cdr.dev/api/v2/users/me/keys/tokens/tokenconfig": ]
server redirected request from https://dev.coder.com/api/v2/users/me/keys/tokens/tokenconfig to https://dogfood.cdr.dev/api/v2/users/me/keys/tokens/tokenconfig
Suggestion: The deployment URL may have changed. Run "coder login https://dogfood.cdr.dev" to log in against the new URL, or pass --allow-redirects to follow redirects.

The codersdk.New default http.Client is intentionally left unchanged to avoid altering behavior for agents, provisioner daemons, and external SDK consumers.

Breaking change

The CLI previously followed redirects silently. Any workflow that depended on that, for example a stale URL config that "worked" through a host redirect, or an http-to-https redirect in front of a deployment, will now fail with the error above until the stored URL is updated with coder login.

To keep the old behavior, opt in with the new global flag --allow-redirects or CODER_ALLOW_REDIRECTS=true. The flag help text notes that following redirects may alter the request method or drop its body, so this is a compatibility escape hatch rather than a recommended setting.

Tests

  • Test_createHTTPClientRedirects/RejectedByDefault: a stale server redirects to a target serving [] on GET; asserts CreateToken fails with *redirectError carrying the correct URLs and that the formatted output includes the login suggestion and the flag name.
  • Test_createHTTPClientRedirects/AllowRedirects: with allowRedirects set, GET requests follow the redirect successfully and the POST reproduces the legacy downgrade behavior without a redirectError.
  • Test_redirectErrorHelper: host change, http-to-https upgrade, and same-deployment cases.

Golden help files and docs/reference/cli/index.md regenerated with make gen.


Generated by Coder Agents on behalf of @f0ssel.

Go's default redirect handling follows a 301/302/303 by downgrading the
request to a GET with no body. With a stale deployment URL that redirects
to the new host, this silently turned "coder tokens create" into a list
request and surfaced a confusing JSON decode error.

Reject redirects in the CLI HTTP client and return a typed error that
names the original and target URLs and suggests "coder login <new URL>"
when the redirect changes host or scheme.
…rects

Rejecting redirects is a breaking change for anyone who was relying on
the old follow-with-downgrade behavior. Add a global --allow-redirects
flag (CODER_ALLOW_REDIRECTS) that restores it, and mention the flag in
the redirect error suggestion.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

@f0ssel f0ssel changed the title fix(cli): error on HTTP redirects instead of following them fix: error on CLI HTTP redirects instead of following them Sep 8, 2026
@f0ssel
f0ssel marked this pull request as ready for review September 8, 2026 16:24

@aslilac aslilac left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

reword/remove that one comment pls, but otherwise I'm definitely in favor

Comment thread cli/root.go Outdated
Comment on lines +907 to +912
// rejectRedirect is an http.Client CheckRedirect hook that refuses to
// follow any redirect. Go's default behavior would follow a 301, 302, or
// 303 by downgrading the request to a GET with no body, silently turning
// a POST into a read of the same path. A redirect from the API almost
// always means the configured deployment URL is stale, so surface that
// instead. The --allow-redirects flag restores the old behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yap

@ethanndickson ethanndickson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One comment re: flag wording otherwise lgtm

Comment thread cli/testdata/coder_--help.golden Outdated

--allow-redirects bool, $CODER_ALLOW_REDIRECTS
Follow HTTP redirects from the server instead of returning an error.
Following a redirect downgrades POST requests to GET and may cause

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a bit too specific. Perhaps best to say "Following redirects may alter the request method and/or drop its body"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree, AFAIK 307 and 308 preserves the request. And in other scenarios it's often client-specific.

@f0ssel
f0ssel merged commit 65d01ef into main Sep 9, 2026
55 of 57 checks passed
@f0ssel
f0ssel deleted the fix/cli-reject-redirects branch September 9, 2026 11:34
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants