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

  1. Check the job/block still exists and is running before sending input (poll exit info).
  2. Unwrap the error with errors.Unwrap to see the RPC root cause and fix that (reconnect, restart job).
  3. Retry the input send once after a short delay if it was a transient RPC timeout.
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/576e264a0bf166e3. Report an issue: GitHub.