Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/API.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
_telemetry_report_exit
_tm_clean_log
_tm_enabled
_tm_explain
_tm_find_errinfo
_tm_payload
_tm_pick_logfile
Expand Down
12 changes: 11 additions & 1 deletion api/errorlog.func
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ write_errinfo() {
# * $1: exit_code
# * $2: log_text (optional override for the output section)
# ------------------------------------------------------------------------------
# The table's curl and APT readings only hold when that tool ran. The error
# handler's guard knows which; the host always has it loaded.
_tm_explain() {
if [[ -n "${2:-}" ]] && declare -f _eh_explanation >/dev/null 2>&1; then
_eh_explanation "$1" "$2"
else
explain_exit_code "$1"
fi
}

build_error_string() {
local exit_code="${1:-1}"
local log_text="${2:-}"
Expand Down Expand Up @@ -305,7 +315,7 @@ build_error_string() {
[[ -z "$cmd" && -n "${FAILED_COMMAND:-}" ]] && cmd="${FAILED_COMMAND}"
[[ -z "$line" && -n "${FAILED_LINE:-}" ]] && line="${FAILED_LINE}"

explanation=$(explain_exit_code "$exit_code")
explanation=$(_tm_explain "$exit_code" "$cmd")

if [[ -n "$cmd" ]]; then
if [[ -n "$line" ]]; then
Expand Down
23 changes: 16 additions & 7 deletions api/exitcodes.func
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,22 @@ categorize_error() {
return
fi

local code="$1"
local code="$1" command="${2:-${FAILED_COMMAND:-}}" errinfo tool
case "$code" in
113 | 114 | 122 | 129 | 130 | 143 | 253 | 254) echo "user_aborted"; return ;;
134 | 137) echo "resource"; return ;;
139 | 141) echo "signal"; return ;;
esac
# The tool that ran says more than a status several tools share.
if [[ -z "$command" ]] && declare -f _tm_find_errinfo >/dev/null 2>&1 && errinfo=$(_tm_find_errinfo); then
command=$(sed -n 's/^COMMAND=//p' "$errinfo" | head -n1)
fi
tool="${command%% *}"
tool="${tool##*/}"
case "$tool" in
systemctl) echo "service"; return ;;
pct | qm | pvesm | pvesh | pveam) echo "proxmox"; return ;;
esac
case "$code" in
6 | 7 | 22 | 35) echo "network" ;;
10) echo "config" ;;
Expand All @@ -284,12 +299,6 @@ categorize_error() {
150 | 151 | 153 | 154) echo "service" ;;
170 | 171 | 172 | 173 | 180 | 181 | 182 | 183 | 190 | 191 | 192 | 193) echo "database" ;;
243 | 245 | 246 | 247 | 248 | 249) echo "runtime" ;;
# Kept in step with the aborted branch of post_update_to_api: a code that
# makes the run "aborted" must not describe itself as something else here.

113 | 114 | 122 | 129 | 130 | 143 | 253 | 254) echo "user_aborted" ;;
134 | 137) echo "resource" ;;
139 | 141) echo "signal" ;;
1 | 2) echo "shell" ;;
*) echo "unknown" ;;
esac
Expand Down
2 changes: 1 addition & 1 deletion api/telemetry.func
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ post_update_to_api() {
# Retry with a smaller error trace (in case the payload was the problem)
if [[ -n "$error_raw" ]]; then
local short_error
short_error="exit_code=${exit_code} | $(explain_exit_code "$exit_code")"
short_error="exit_code=${exit_code} | $(_tm_explain "$exit_code" "${FAILED_COMMAND:-}")"
[[ -n "${FAILED_COMMAND:-}" ]] && short_error+=" | at line ${FAILED_LINE:-?}: $(printf '%s' "$FAILED_COMMAND" | head -c 200)"
payload=$(_tm_payload "$pb_status" "$exit_code" "$short_error")
if _tm_send "$payload" "$STATUS_TIMEOUT" 2; then
Expand Down
3 changes: 3 additions & 0 deletions core/error_handler.func
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ _eh_explanation() {
if [[ -n "$tools" ]] && ! _eh_command_is "$command" "$tools"; then
explanation="Command failed with exit status ${exit_code}"
fi
if [[ "$exit_code" == 5 ]] && _eh_command_is "$command" "systemctl"; then
explanation="Systemd: unit not found"
fi
printf '%s' "$explanation"
}

Expand Down
6 changes: 5 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ terminal status: it writes `/root/.install-<SESSION_ID>.failed` and an

`explain_exit_code` — the exit code table — and `categorize_error`. This is the
authoritative copy of the table; `core/error_handler.func` carries a fallback
for the container case where this file is not loaded.
for the container case where this file is not loaded. The table borrows curl's
and APT's numbering, so the telemetry summary (`_tm_explain` in
`errorlog.func`) runs it through the handler's tool guard when the failing
command is known, and `categorize_error` files `systemctl` and `pct`/`qm`
failures under their own categories rather than the code's.

## [`errorlog.func`](../api/errorlog.func)

Expand Down
20 changes: 19 additions & 1 deletion tests/exit-code-hints.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
set -uo pipefail

CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
eval "$(awk '/^explain_exit_code[(][)] [{]/,/^}/' "$CORE_DIR/api/exitcodes.func")"
eval "$(awk '/^(explain_exit_code|categorize_error)[(][)] [{]/,/^}/' "$CORE_DIR/api/exitcodes.func")"
eval "$(awk '/^_eh_(hint_from_log|explanation|tools_for|command_is)[(][)] [{]/,/^}/' "$CORE_DIR/core/error_handler.func")"
eval "$(awk '/^(_tm_explain|build_error_string)[(][)] [{]/,/^}/' "$CORE_DIR/api/errorlog.func")"
_tm_find_errinfo() { return 1; }
get_error_log() { :; }

TEST_DIR="$(mktemp -d)"
trap 'rm -rf -- "$TEST_DIR"' EXIT
Expand Down Expand Up @@ -37,6 +40,21 @@ expect "node 243 keeps the Node text" Node.js "$(_eh_explanation 243 'node serve
expect "python 243 does not" "Command failed with exit status 243" "$(_eh_explanation 243 'python3 app.py')"
expect "core codes stay as they are" Proxmox "$(_eh_explanation 209 '' | cut -d: -f1)"
expect "unprefixed codes stay as they are" "Command not found" "$(_eh_explanation 127 'foo')"
expect "systemctl 5 is a missing unit" "Systemd: unit not found" "$(_eh_explanation 5 'systemctl start portainer')"

# The telemetry summary goes through the same guard when the command is known.
expect "the summary does not call systemctl 5 a proxy error" "exit_code=5 | Systemd: unit not found | at line 76: systemctl start portainer" "$(FAILED_COMMAND='systemctl start portainer' FAILED_LINE=76 build_error_string 5)"
expect "pct exec 255 is not a DPKG error" "exit_code=255 | Command failed with exit status 255 | at line 12: pct exec 100 -- ip a" "$(FAILED_COMMAND='pct exec 100 -- ip a' FAILED_LINE=12 build_error_string 255)"
expect "apt 100 keeps the APT text" APT "$(FAILED_COMMAND='apt install -y grafana' FAILED_LINE=31 build_error_string 100 | cut -d'|' -f2 | cut -d: -f1 | tr -d ' ')"
expect "without a command the table stands" "exit_code=255 | DPKG: Fatal internal error" "$(FAILED_COMMAND= build_error_string 255)"

# The category follows the tool where the code is shared.
expect "systemctl is a service failure" service "$(categorize_error 5 'systemctl start portainer')"
expect "pct exec is a Proxmox failure" proxmox "$(categorize_error 255 'pct exec 100 -- ip a')"
expect "a kill stays a resource failure whatever ran" resource "$(categorize_error 137 'pct exec 100 -- cargo build')"
expect "apt keeps its category" dependency "$(categorize_error 100 'apt install -y grafana')"
expect "an abort stays an abort" user_aborted "$(categorize_error 130 'systemctl start x')"
expect "no command: the table" shell "$(FAILED_COMMAND= categorize_error 1)"

: >"$WARN"
printf '%s\n' 'Scope: all 36 workspace projects' ' ERR_PNPM_OUTDATED_LOCKFILE Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with <ROOT>/server/package.json' >"$LOG"
Expand Down
1 change: 1 addition & 0 deletions ui/API.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ _stop_container_if_installing
_telemetry_report_exit
_tm_clean_log
_tm_enabled
_tm_explain
_tm_find_errinfo
_tm_payload
_tm_pick_logfile
Expand Down
Loading