sipeed/picoclaw · critical

failed to generate PKCE: %v

Error message

failed to generate PKCE: %v

What it means

Returned as HTTP 500 by POST /api/oauth/login (browser method) when auth.GeneratePKCE fails. GeneratePKCE does a single crypto/rand.Read of 64 bytes and builds the PKCE verifier/challenge; on Linux it fails only when the kernel CSPRNG is unavailable (getrandom(2) error), which is essentially a broken or massively restricted runtime. No input from the request can cause it.

Source

Thrown at web/backend/api/oauth.go:279

			"method":     method,
			"flow_id":    flow.ID,
			"user_code":  flow.UserCode,
			"verify_url": flow.VerifyURL,
			"interval":   flow.Interval,
			"expires_at": flow.ExpiresAt.Format(time.RFC3339),
		})
		return

	case oauthMethodBrowser:
		cfg, err := oauthConfigForProvider(provider)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		pkce, err := oauthGeneratePKCE()
		if err != nil {
			http.Error(w, fmt.Sprintf("failed to generate PKCE: %v", err), http.StatusInternalServerError)
			return
		}
		state, err := oauthGenerateState()
		if err != nil {
			http.Error(w, fmt.Sprintf("failed to generate state: %v", err), http.StatusInternalServerError)
			return
		}

		redirectURI := buildOAuthRedirectURI(r)
		authURL := oauthBuildAuthorizeURL(cfg, pkce, state, redirectURI)

		now := oauthNow()
		flow := &oauthFlow{
			ID:           newOAuthFlowID(),
			Provider:     provider,
			Method:       method,
			Status:       oauthFlowPending,
			CreatedAt:    now,

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Verify the CSPRNG from inside the same environment: head -c 32 /dev/urandom | xxd (or a tiny Go program doing crypto/rand.Read).
  2. Loosen the container/sandbox profile so getrandom(2)//dev/urandom is permitted.
  3. On VMs that boot before entropy init, ensure the VM has virtio-rng or wait a few seconds after boot and retry the login request.
  4. If the environment fundamentally cannot provide a CSPRNG, use token login instead (method "token"), which does not call rand during the request.

Example fix

// before: sandbox blocks getrandom
POST /api/oauth/login {"provider":"openai","method":"browser"}
// -> 500 failed to generate PKCE: crypto/rand failed

// after: docker run --security-opt seccomp=unconfined ... # or fix the profile to allow getrandom
POST /api/oauth/login {"provider":"openai","method":"browser"}
// -> 200 {"auth_url":"https://auth.openai.com/..."}
Defensive patterns

Strategy: fallback

Try / catch

const res = await startBrowserLogin(provider);
if (res.status === 500) {
  const { message } = await res.json();
  if (/generate PKCE/i.test(message ?? '')) {
    // host CSPRNG is broken: fall back to token login (no randomness needed per request)
    return startTokenLogin(provider);
  }
  throw new Error(message);
}

Prevention

When it happens

Trigger: POST /api/oauth/login {"provider":"openai","method":"browser"} on a host where crypto/rand.Read returns an error — e.g. a seccomp/container profile blocking getrandom, an extremely early-boot VM before the entropy pool is initialized, or a broken /dev/urandom.

Common situations: Over-restrictive gVisor/Firecracker/seccomp sandboxes; exotic embedded kernels; virtually never on normal Linux/macOS dev machines or standard Docker.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/9bcd5ca692a5f1ae. Report an issue: GitHub.