From 20b061902be19f49cba6ee9dcdc487b2f42ee78e Mon Sep 17 00:00:00 2001 From: Dan Finn Date: Mon, 3 Aug 2026 16:22:11 -0400 Subject: [PATCH] fix(cli): surface actionable errors for cloud-mode setup failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user picks Cloud mode during `pad init` but doesn't have a Pad Cloud account, the CLI hits the cloud server and surfaces raw server errors like "Missing CSRF token" — which is an implementation detail that gives no indication of what went wrong or how to fix it. This patch: - Splits the ModeCloud and ModeRemote branches in printSetupRequiredHint so cloud users see "sign up or switch to local" instead of the generic "run pad auth setup on the server" message. - Returns a cloud-specific error from pad init when setup_required is true in cloud mode. - Intercepts csrf_error responses in the CLI HTTP client and replaces the raw server message with an actionable "run pad auth login" message, since the CLI never sends CSRF cookies and this error always indicates a stale or mismatched session. --- cmd/pad/cmd_auth.go | 9 +++++++-- cmd/pad/init.go | 3 +++ internal/cli/client.go | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cmd/pad/cmd_auth.go b/cmd/pad/cmd_auth.go index 3050adc7..a4475375 100644 --- a/cmd/pad/cmd_auth.go +++ b/cmd/pad/cmd_auth.go @@ -691,11 +691,16 @@ func saveCredentials(cfg *config.Config, resp *cli.LoginResponse) error { } func printSetupRequiredHint(cfg *config.Config) { - fmt.Println("This Pad instance has not been initialized yet.") switch cfg.Mode { - case config.ModeRemote, config.ModeCloud: + case config.ModeCloud: + fmt.Println("You're configured in Cloud mode but don't have an account on Pad Cloud yet.") + fmt.Printf(" Sign up at %s/register, then run 'pad auth login'.\n", config.CloudBaseURL) + fmt.Println(" Or switch to a local server with 'pad auth configure --mode local'.") + case config.ModeRemote: + fmt.Println("This Pad instance has not been initialized yet.") fmt.Println("Run 'pad auth setup' on the machine or container running the Pad server, then try again.") default: + fmt.Println("This Pad instance has not been initialized yet.") fmt.Println("Run 'pad auth setup' to create the first admin account, then try again.") } } diff --git a/cmd/pad/init.go b/cmd/pad/init.go index bdc9ba4e..44219d32 100644 --- a/cmd/pad/init.go +++ b/cmd/pad/init.go @@ -198,6 +198,9 @@ Examples: // --cli-prompt requires a TTY there too. Use --email/--name/ // --password to bootstrap a remote instance non-interactively. printSetupRequiredHint(cfg) + if cfg.Mode == config.ModeCloud { + return fmt.Errorf("Pad Cloud account required") + } return fmt.Errorf("this Pad instance has not been initialized yet") } else if !canPromptForConfig() { printSetupRequiredHint(cfg) diff --git a/internal/cli/client.go b/internal/cli/client.go index 7c935b6b..729f6887 100644 --- a/internal/cli/client.go +++ b/internal/cli/client.go @@ -1612,6 +1612,9 @@ func parseErrorBody(status int, body []byte) error { Error APIError `json:"error"` } if err := json.Unmarshal(body, &errResp); err == nil && errResp.Error.Message != "" { + if errResp.Error.Code == "csrf_error" { + errResp.Error.Message = "Session authentication error. Run 'pad auth login' to re-authenticate." + } return &errResp.Error } return fmt.Errorf("API error: %d %s", status, string(body))