fix: improve error messages with actionable guidance for common failures (#452)

- Add signal exit code handling (130/Ctrl+C, 137/killed, 255/SSH failure, 2/syntax error)
- Replace vague "Cloud API retry logic exhausted" with attempt count and retry advice
- Add network troubleshooting hint to API network error after retries
- Clarify OAuth fallback prompt: explain why OAuth failed and what happens next
- Consolidate auth cancellation message with three clear recovery options

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 06:26:19 -08:00 committed by GitHub
parent f2c9af0d79
commit 55fd4022e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 29 additions and 12 deletions

View file

@ -424,6 +424,17 @@ function reportDownloadError(ghUrl: string, err: unknown): never {
export function getScriptFailureGuidance(exitCode: number | null, cloud: string): string[] {
switch (exitCode) {
case 130:
return ["Script was interrupted (Ctrl+C). No server was left running."];
case 137:
return ["Script was killed (likely by the system due to timeout or out of memory)."];
case 255:
return [
"SSH connection failed. Common causes:",
" - Server is still booting (wait a moment and retry)",
" - Firewall blocking SSH port 22",
" - Server was terminated before the session started",
];
case 127:
return [
"A required command was not found. Check that these are installed:",
@ -432,6 +443,11 @@ export function getScriptFailureGuidance(exitCode: number | null, cloud: string)
];
case 126:
return ["A command was found but could not be executed (permission denied)."];
case 2:
return [
"Shell syntax or argument error. This is likely a bug in the script.",
` Report it at: ${pc.cyan(`https://github.com/OpenRouterTeam/spawn/issues`)}`,
];
case 1:
return [
"Common causes:",

View file

@ -747,14 +747,13 @@ get_openrouter_api_key_oauth() {
# OAuth failed, offer manual entry
echo ""
log_warn "OAuth authentication was not completed."
log_info "You can enter your API key manually instead."
log_info "Get a free key at: https://openrouter.ai/settings/keys"
log_warn "Browser-based OAuth login was not completed (timed out or browser not available)."
log_info "You can paste an API key instead. Create one at: https://openrouter.ai/settings/keys"
echo ""
local manual_choice
manual_choice=$(safe_read "Would you like to enter your API key manually? (Y/n): ") || {
manual_choice=$(safe_read "Paste your API key manually? (Y/n): ") || {
log_error "Cannot prompt for manual entry in non-interactive mode"
log_warn "Set OPENROUTER_API_KEY environment variable for non-interactive usage"
log_warn "Set OPENROUTER_API_KEY environment variable before running spawn"
return 1
}
@ -763,12 +762,11 @@ get_openrouter_api_key_oauth() {
echo "${api_key}"
return 0
else
log_error "Authentication cancelled by user"
log_error ""
log_error "An OpenRouter API key is required to use spawn."
log_error "Get your free API key at: https://openrouter.ai/settings/keys"
log_error ""
log_error "For non-interactive usage, set: OPENROUTER_API_KEY=sk-or-v1-..."
log_error "Authentication cancelled. An OpenRouter API key is required to use spawn."
log_warn "To authenticate, either:"
log_warn " - Re-run this command and complete the OAuth flow in your browser"
log_warn " - Set OPENROUTER_API_KEY=sk-or-v1-... before running spawn"
log_warn " - Create a key at: https://openrouter.ai/settings/keys"
return 1
fi
}
@ -1112,6 +1110,7 @@ _handle_api_transient_error() {
if [[ "${error_type}" == "network" ]]; then
if ! _api_should_retry_on_error "network" "${attempt}" "${max_retries}" "${!interval_var}" "${!max_interval_var}" "Cloud API network error"; then
log_error "Cloud API network error after ${max_retries} attempts"
log_warn "Check your internet connection and verify the provider's API is reachable."
return 1
fi
else
@ -1166,7 +1165,9 @@ _cloud_api_retry_loop() {
return 0
done
log_error "Cloud API retry logic exhausted"
log_error "Cloud API request failed after ${max_retries} attempts"
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
}