A provider's errors surface in job results, the audit log and the CLI. They read as four different systems.
What is there
sysctl "sysctl create: ..." <domain> <verb>
netplan "netplan apply: ..." <domain> <verb>
ntp "ntp: ..." <domain>
service "service: ..." <domain>
certificate "create certificate: ..." <verb> <domain>
cron "create cron entry: ..." <verb> <domain>
file "failed to execute template: ..." failed to <verb>
user "chpasswd failed: ..." <command> failed
Reproduce:
for d in node/sysctl node/ntp node/user node/service node/certificate \
scheduled/cron file network/netplan; do
printf '%-20s ' "$d"
grep -rho 'fmt.Errorf("[a-z][a-z ]*:' internal/provider/$d/*.go \
| sed 's/fmt.Errorf("//' | sort -u | head -3 | tr '\n' ' '
echo
done
Why it matters beyond tidiness
file's failed to ... prefix is the one worth changing on its own merits. These errors are almost always wrapped by a caller, so the rendered string becomes "deploy cron entry: failed to execute template: ...". Go's own guidance is that error strings do not announce failure, because the context does.
user's chpasswd failed: names the command rather than the operation, which leaks the implementation into a message a CLI user reads.
Not in scope
The sentinel errors are already consistent and should stay as they are:
ErrUnsupported = errors.New("operation not supported on this OS family")
ErrNotFound = errors.New("not found")
ErrNotManaged = errors.New("not managed by osapi")
ErrNotInstalled = errors.New("not installed")
So is wrapping with %w, which every provider does on the paths that carry a cause. #536 fixed the one place that did not.
Suggested convention
<domain> <verb>: <what went wrong>, which is what sysctl and netplan already use and which sorts and greps well:
sysctl create: key must not be empty
cron delete: not managed by osapi
file deploy: execute template: ...
Picking one and applying it is the whole change. It should be settled in components/osapi/providers.md first so new providers inherit it rather than copying whichever neighbour they read.
A provider's errors surface in job results, the audit log and the CLI. They read as four different systems.
What is there
Reproduce:
Why it matters beyond tidiness
file'sfailed to ...prefix is the one worth changing on its own merits. These errors are almost always wrapped by a caller, so the rendered string becomes "deploy cron entry: failed to execute template: ...". Go's own guidance is that error strings do not announce failure, because the context does.user'schpasswd failed:names the command rather than the operation, which leaks the implementation into a message a CLI user reads.Not in scope
The sentinel errors are already consistent and should stay as they are:
So is wrapping with
%w, which every provider does on the paths that carry a cause. #536 fixed the one place that did not.Suggested convention
<domain> <verb>: <what went wrong>, which is what sysctl and netplan already use and which sorts and greps well:Picking one and applying it is the whole change. It should be settled in
components/osapi/providers.mdfirst so new providers inherit it rather than copying whichever neighbour they read.