fix: improve error messages and UX consistency across CLI and shell scripts (#466)

- Clarify download error messages: distinguish HTTP errors from network errors
  with specific status codes in the message
- Add actionable next steps to OAuth timeout: re-run command or set key manually
- Standardize error help labels to "How to fix:" across CLI and shell scripts
  (was inconsistently "What to do:", "Troubleshooting:", or missing)
- Add API method/endpoint context to retry failure messages so users know
  which API call failed
- Make verify_agent_installed error cases mutually exclusive: first for
  PATH/installation issues, second for runtime/dependency issues

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
A 2026-02-11 07:46:56 -08:00 committed by GitHub
parent d47bbbd592
commit d9037fad32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 37 additions and 25 deletions

View file

@ -392,30 +392,33 @@ async function downloadScriptWithFallback(primaryUrl: string, fallbackUrl: strin
}
function reportDownloadFailure(primaryUrl: string, fallbackUrl: string, primaryStatus: number, fallbackStatus: number): void {
p.log.error("Script download failed");
if (primaryStatus === 404 && fallbackStatus === 404) {
p.log.error("Script download failed (HTTP 404: not found)");
console.error("\nThe script file could not be found.");
console.error("This usually means the combination hasn't been published yet,");
console.error("even though it may appear in the matrix.");
console.error(`\nWhat to do:`);
console.error(`\nHow to fix:`);
console.error(` 1. Verify the combination is implemented: ${pc.cyan("spawn list")}`);
console.error(` 2. Try again later (the script may be deploying)`);
console.error(` 3. Report the issue: ${pc.cyan(`https://github.com/${REPO}/issues`)}`);
} else {
console.error(`\nNetwork or server error (HTTP ${primaryStatus}) - try again in a few moments.`);
p.log.error(`Script download failed (HTTP ${primaryStatus}/${fallbackStatus})`);
console.error(`\nBoth download sources returned errors.`);
if (primaryStatus >= 500 || fallbackStatus >= 500) {
console.error("The server may be experiencing temporary issues.");
}
console.error(`\nHow to fix:`);
console.error(` 1. Wait a moment and try again`);
console.error(` 2. Check GitHub status: ${pc.cyan("https://www.githubstatus.com")}`);
}
}
function reportDownloadError(ghUrl: string, err: unknown): never {
p.log.error("Failed to download spawn script");
p.log.error("Script download failed (network error)");
console.error("\nError:", getErrorMessage(err));
console.error("\nTroubleshooting:");
console.error(` 1. Verify this combination exists: ${pc.cyan("spawn list")}`);
console.error(" 2. Check your internet connection");
console.error("\nHow to fix:");
console.error(" 1. Check your internet connection");
console.error(` 2. Verify this combination exists: ${pc.cyan("spawn list")}`);
console.error(` 3. Try accessing the script directly: ${ghUrl}`);
process.exit(1);
}
@ -927,7 +930,7 @@ export async function cmdUpdate(): Promise<void> {
} catch (err) {
s.stop(pc.red(`Failed to check for updates ${pc.dim(`(current: v${VERSION})`)}`));
console.error("Error:", getErrorMessage(err));
console.error(`\nTroubleshooting:`);
console.error(`\nHow to fix:`);
console.error(` 1. Check your internet connection`);
console.error(` 2. Try again in a few moments`);
console.error(` 3. Update manually: ${pc.cyan(`curl -fsSL ${RAW_BASE}/cli/install.sh | bash`)}`);

View file

@ -178,7 +178,7 @@ export async function loadManifest(forceRefresh = false): Promise<Manifest> {
throw new Error(
`Cannot load manifest: failed to fetch from GitHub and no local cache available.\n` +
`\n` +
`Troubleshooting:\n` +
`How to fix:\n` +
` 1. Check your internet connection\n` +
` 2. Try again in a few moments (GitHub may be temporarily unreachable)\n` +
` 3. If the problem persists, clear the cache and retry:\n` +

View file

@ -114,7 +114,7 @@ _report_modal_create_error() {
log_error " - Network connectivity issues"
log_error " - Invalid sandbox name (must be alphanumeric with dashes)"
log_error ""
log_error "Troubleshooting:"
log_error "How to fix:"
log_error " 1. Re-authenticate: modal setup"
log_error " 2. Verify account status: https://modal.com/settings"
log_error " 3. Check Modal status: https://status.modal.com"

View file

@ -704,10 +704,15 @@ _await_oauth_callback() {
if ! _wait_for_oauth "${code_file}"; then
cleanup_oauth_session "${server_pid}" "${oauth_dir}"
log_warn "OAuth timed out after 120 seconds. Common causes:"
log_warn "OAuth timed out after 120 seconds. Possible causes:"
log_warn " - Browser did not open (try visiting the URL manually)"
log_warn " - Authentication was not completed in the browser"
log_warn " - Firewall or proxy blocked the local callback on port ${actual_port}"
log_warn ""
log_warn "How to fix:"
log_warn " 1. Re-run the command to try again"
log_warn " 2. Set the key manually: export OPENROUTER_API_KEY=sk-or-..."
log_warn " (get a key at https://openrouter.ai/keys)"
return 1
fi
@ -1155,11 +1160,12 @@ _handle_api_transient_error() {
# Example: generic_cloud_api "$DO_API_BASE" "$DO_API_TOKEN" GET "/account" "" 5
# Retries on: 429 (rate limit), 503 (service unavailable), network errors
# Internal retry loop shared by generic_cloud_api and generic_cloud_api_custom_auth
# Usage: _cloud_api_retry_loop REQUEST_FUNC MAX_RETRIES [REQUEST_FUNC_ARGS...]
# Usage: _cloud_api_retry_loop REQUEST_FUNC MAX_RETRIES API_DESCRIPTION [REQUEST_FUNC_ARGS...]
_cloud_api_retry_loop() {
local request_func="${1}"
local max_retries="${2}"
shift 2
local api_description="${3}"
shift 3
local attempt=1
local interval=2
@ -1187,7 +1193,7 @@ _cloud_api_retry_loop() {
return 0
done
log_error "Cloud API request failed after ${max_retries} attempts"
log_error "Cloud API request failed after ${max_retries} attempts (${api_description})"
log_warn "This is usually caused by rate limiting or temporary provider issues."
log_warn "Wait a minute and try again, or check the provider's status page."
return 1
@ -1201,7 +1207,7 @@ generic_cloud_api() {
local body="${5:-}"
local max_retries="${6:-3}"
_cloud_api_retry_loop _make_api_request "${max_retries}" "${base_url}" "${auth_token}" "${method}" "${endpoint}" "${body}"
_cloud_api_retry_loop _make_api_request "${max_retries}" "${method} ${endpoint}" "${base_url}" "${auth_token}" "${method}" "${endpoint}" "${body}"
}
# Helper to make API request with custom curl auth args (e.g., Basic Auth, custom headers)
@ -1249,7 +1255,7 @@ generic_cloud_api_custom_auth() {
shift 5
# Remaining args are custom curl auth flags
_cloud_api_retry_loop _make_api_request_custom_auth "${max_retries}" "${base_url}${endpoint}" "${method}" "${body}" "$@"
_cloud_api_retry_loop _make_api_request_custom_auth "${max_retries}" "${method} ${endpoint}" "${base_url}${endpoint}" "${method}" "${body}" "$@"
}
# ============================================================
@ -1275,23 +1281,26 @@ verify_agent_installed() {
log_error ""
log_error "Possible causes:"
log_error " - The installation script encountered an error (check logs above)"
log_error " - Network connectivity issues during download"
log_error " - Insufficient disk space or permissions"
log_error " - The binary was installed to a directory not in PATH"
log_error " - Network issues prevented the download from completing"
log_error ""
log_error "Try running the script again, or install ${agent_name} manually."
log_error "How to fix:"
log_error " 1. Re-run the script to retry the installation"
log_error " 2. Install ${agent_name} manually and ensure it is in PATH"
return 1
fi
if ! "${agent_cmd}" "${verify_arg}" &> /dev/null; then
log_error "${agent_name} installation failed: '${agent_cmd} ${verify_arg}' returned an error"
log_error "${agent_name} verification failed: '${agent_cmd} ${verify_arg}' returned an error"
log_error ""
log_error "The command was installed but doesn't execute properly."
log_error "The command exists but does not run correctly."
log_error "Possible causes:"
log_error " - Missing runtime dependencies (Python, Node.js, etc.)"
log_error " - Incompatible system architecture or OS version"
log_error " - Corrupted download or partial installation"
log_error ""
log_error "Try running the script again, or check ${agent_name}'s installation docs."
log_error "How to fix:"
log_error " 1. Check ${agent_name}'s installation docs for prerequisites"
log_error " 2. Run '${agent_cmd} ${verify_arg}' manually to see the error"
return 1
fi

View file

@ -144,7 +144,7 @@ verify_sprite_connectivity() {
log_error "Sprite '${sprite_name}' failed to respond after ${max_attempts} attempts"
log_error ""
log_error "Troubleshooting:"
log_error "How to fix:"
log_error " 1. Check sprite status: sprite list"
log_error " 2. View sprite logs: sprite logs ${sprite_name}"
log_error " 3. Try recreating the sprite: sprite delete ${sprite_name} && sprite create ${sprite_name}"