ory/hydra · error

device_challenge is required

Error message

device_challenge is required

What it means

The device-flow local UI (deviceSrv.GETdevice) serves the user-code page and requires the device_challenge query parameter to build the accept URL later. When it is missing, the handler responds 400 'device_challenge is required'.

Source

Thrown at cmd/cmd_perform_device_flow.go:159

	d := deviceSrv{cl: cl, mux: http.NewServeMux()}
	d.mux.HandleFunc("GET /device", d.GETdevice)
	d.mux.HandleFunc("POST /device", d.POSTdevice)
	d.mux.HandleFunc("GET /device/done", d.GETdone)
	return &d
}

type deviceSrv struct {
	cl  *openapi.APIClient
	mux *http.ServeMux
}

func (s *deviceSrv) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.mux.ServeHTTP(w, r)
}

func (s *deviceSrv) GETdevice(w http.ResponseWriter, r *http.Request) {
	if r.URL.Query().Get("device_challenge") == "" {
		http.Error(w, "device_challenge is required", http.StatusBadRequest)
		return
	}

	err := userCodeTemplate.Execute(w, userCodeData{
		UserCode:        r.URL.Query().Get("user_code"),
		DeviceChallenge: r.URL.Query().Get("device_challenge"),
	})
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to render template: %s", err), http.StatusInternalServerError)
		return
	}
}

func (s *deviceSrv) POSTdevice(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		http.Error(w, fmt.Sprintf("Failed to parse form: %s", err), http.StatusBadRequest)
		return
	}

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Open the URL exactly as returned in verification_uri_complete (or append the device_challenge query param yourself)
  2. Re-run the device flow login to obtain a fresh device challenge and verification URL
  3. Check that the browser/QR scanner did not strip the query string
  4. Ensure nothing in the environment (proxy, redirect) rewrites the URL

Example fix

// before
browser.Open("http://localhost:4445/device")
// after
browser.Open(fmt.Sprintf("http://localhost:4445/device?device_challenge=%s&user_code=%s",
	url.QueryEscape(challenge), url.QueryEscape(userCode)))
Defensive patterns

Strategy: validation

Validate before calling

// client side: build the URL with the challenge before opening
if deviceChallenge == "" {
	return errors.New("device challenge missing; rerun the device flow")
}
verifyURL := fmt.Sprintf("%s?device_challenge=%s", verifyBase, url.QueryEscape(deviceChallenge))

Prevention

When it happens

Trigger: Opening the device verification URL without appending ?device_challenge=..., or with an empty value, as produced by Hydra's device authorization response (verification_uri_complete).

Common situations: User manually typing the verification URL from docs instead of using verification_uri_complete; URL truncation by a terminal/QR-code scanner; the CLI skipping the device_challenge query param when opening the browser; proxy or link shortener stripping query parameters.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/f3fb0aaf76c2b222. Report an issue: GitHub.