wavetermdev/waveterm · error
failed to send input to job: %w
Error message
failed to send input to job: %w
What it means
This error wraps a failure from wshclient.JobInputCommand, which sends terminal keyboard input to a running background job's PTY over the Wave RPC channel. The library throws it when the RPC round-trip to the job's input endpoint fails (connection issue, job already gone, or underlying PTY write error). It preserves the underlying cause via %w so callers can unwrap it.
Source
Thrown at pkg/jobcontroller/jobcontroller.go:1535
log.Printf("[job:%s] warning: failed to update termsize in DB: %v", jobId, err)
}
}
_, err := CheckJobConnected(ctx, jobId)
if err != nil {
return err
}
rpcOpts := &wshrpc.RpcOpts{
Route: wshutil.MakeJobRouteId(jobId),
Timeout: 5000,
NoResponse: false,
}
bareRpc := wshclient.GetBareRpcClient()
err = wshclient.JobInputCommand(bareRpc, data, rpcOpts)
if err != nil {
return fmt.Errorf("failed to send input to job: %w", err)
}
return nil
}
func resetTerminalState(logCtx context.Context, blockId string) {
if blockId == "" {
return
}
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancelFn()
if isFileEmpty(ctx, blockId) {
return
}
blocklogger.Debugf(logCtx, "[conndebug] resetTerminalState: resetting terminal state for block\n")
resetSeq := shellutil.GetTerminalResetSeq()
resetSeq += "\r\n"
err := doWFSAppend(ctx, waveobj.MakeORef(waveobj.OType_Block, blockId), JobOutputFileName, []byte(resetSeq))View on GitHub (pinned to a4447c1563)
Solutions
- Check the job/block still exists and is running before sending input (poll exit info).
- Unwrap the error with errors.Unwrap to see the RPC root cause and fix that (reconnect, restart job).
- Retry the input send once after a short delay if it was a transient RPC timeout.
- If the process exited, close/relaunch the block instead of sending input.
Example fix
// before
err = wshclient.JobInputCommand(bareRpc, data, rpcOpts)
if err != nil {
return fmt.Errorf("failed to send input to job: %w", err)
}
// after
err = wshclient.JobInputCommand(bareRpc, data, rpcOpts)
if err != nil {
if errors.Is(err, rpcclient.ErrConnectionClosed) {
return fmt.Errorf("job rpc connection closed, cannot send input: %w", err)
}
return fmt.Errorf("failed to send input to job: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: check the job is still live before sending input
if exited, _ := jobCmd.GetExitInfo(); exited {
return errors.New("job already exited; not sending input")
} Type guard
func hasActiveProcess(jm *jobmanager.JobCmd) bool {
exited, _ := jm.GetExitInfo()
return !exited
} Try / catch
err := wshclient.JobInputCommand(bareRpc, data, rpcOpts)
if err != nil {
var rpcErr *rpcclient.RpcError
if errors.As(err, &rpcErr) {
// reconnect or report job unavailable
}
return fmt.Errorf("failed to send input to job: %w", err)
} Prevention
- Track job-exited events and stop sending input once fired
- Always unwrap the wrapped cause to distinguish connection vs process errors
- Debounce input RPCs so stale input isn't delivered after exit
When it happens
Trigger: Calling the block input path (e.g. user types into a block backed by a job) while the JobController forwards data via JobInputCommand and the RPC returns an error: job not found, RPC timeout, or the job's HandleInput rejecting input (e.g. 'no active process').
Common situations: Typing into a terminal block whose underlying process has already exited but whose block is still displayed; the wsh server connection dropped; sending input before the job's PTY is fully initialized.
Related errors
- creating new terminal block: %w
- error getting terminal scrollback: %w
- failed to get terminal scrollback: %w
- invalid term size: %v
- no active pty
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/576e264a0bf166e3.
Report an issue: GitHub.