Errors
gorpitx returns wrapped sentinel errors. Match them with errors.Is, never by comparing strings. The package’s own sentinels are in errors.go; the rest come from github.com/psyb0t/common-go/errors, usually imported as commonerrors.
Execution errors
| Sentinel | Text | Returned when |
|---|---|---|
gorpitx.ErrExecuting | RPITX is busy executing another command | Exec is called while another module runs |
gorpitx.ErrUnknownModule | unknown module | The module name is not registered, wrapped with the name |
gorpitx.ErrNotExecuting | RPITX is not executing a command | Stop is called while nothing runs |
commonerrors.ErrTimeout | timeout | The Exec timeout passed and the process was stopped |
commonerrors.ErrTerminated | terminated | Stop ended the process with SIGTERM; both Stop and the blocked Exec return it wrapped |
commonerrors.ErrKilled | killed | The process needed SIGKILL, or ctx was cancelled during Exec |
commonerrors.ErrFailed | failed | The program exited by itself with a non-zero status |
A process that exits by itself with a non-zero status comes back from Exec wrapped with failed to wait for process, as commonerrors.ErrFailed (failed) with the exit code and the process’s stderr in the message: (exit 127): <stderr text>. A process that cannot start at all, for example because stdbuf is missing, comes back wrapped with failed to start process. A missing rpitx binary shows up differently: stdbuf starts, cannot run it, and exits with status 127 (see Troubleshooting).
Validation errors
Validation runs inside Exec before anything starts, and the error comes back wrapped with failed to parse args.
| Sentinel | Text | Returned when |
|---|---|---|
commonerrors.ErrRequiredFieldNotSet | required field is not set | A required field is missing or blank, wrapped with the JSON field name, such as message or messages |
commonerrors.ErrInvalidValue | invalid value | A value is out of range or not in the allowed set, wrapped with the rule and the value you sent |
commonerrors.ErrFileNotFound | file not found | A file field points at a path that does not exist |
gorpitx.ErrFreqOutOfRange | frequency out of RPiTX range | The frequency is outside 5 kHz to 1500 MHz |
gorpitx.ErrFreqPrecision | frequency precision too high | A pifmrds frequency has more than one decimal place |
gorpitx.ErrPIInvalidHex | PI code must be valid hex | A pifmrds PI code has 4 characters that are not hex |
gorpitx.ErrPSTooLong | PS text must be 8 characters or less | A pifmrds station name is too long |
A missing frequency is reported as ErrInvalidValue (frequency must be positive, got: 0.000000) in every module except pifmrds, where it is ErrRequiredFieldNotSet. JSON that does not fit the struct, such as a string where a number belongs, comes back wrapped with failed to unmarshal args.
Reading the text
Errors are wrapped with ctxerrors, so each wrap adds the file, line and function where it happened. A refused tune frequency reads like this, on one line:
failed to parse args: (5 kHz to 1500 MHz), got: 4000.000000 Hz: frequency out of RPiTX range [/path/to/gorpitx/tune.go:90 in github.com/psyb0t/gorpitx.(*TUNE).validateFreq] [/path/to/gorpitx/gorpitx.go:167 in github.com/psyb0t/gorpitx.(*RPITX).prepareCommand]
That suffix is for your logs. Do not show the raw text to end users, and do not parse it.
Matching in code
err := rpitx.Exec(ctx, gorpitx.ModuleNameTUNE, args, 10*time.Second)
switch {
case err == nil:
// finished on its own
case errors.Is(err, commonerrors.ErrTimeout):
// ran for the full 10 seconds, then stopped
case errors.Is(err, gorpitx.ErrExecuting):
// something else is transmitting, try later
case errors.Is(err, commonerrors.ErrInvalidValue),
errors.Is(err, commonerrors.ErrRequiredFieldNotSet),
errors.Is(err, commonerrors.ErrFileNotFound),
errors.Is(err, gorpitx.ErrFreqOutOfRange):
// bad arguments, nothing was transmitted
default:
// the process failed
}