hashicorp/nomad · error
first message should always be setup
Error message
first message should always be setup
What it means
The ExecTaskStreaming protocol requires the very first message on the stream to carry a Setup field (task id, command, tty). If the received message has Setup == nil, the client violated the protocol, so the server rejects it with this error.
Source
Thrown at plugins/drivers/server.go:323
return nil, err
}
resp := &proto.ExecTaskResponse{
Stdout: result.Stdout,
Stderr: result.Stderr,
Result: exitResultToProto(result.ExitResult),
}
return resp, nil
}
func (b *driverPluginServer) ExecTaskStreaming(server proto.Driver_ExecTaskStreamingServer) error {
msg, err := server.Recv()
if err != nil {
return fmt.Errorf("failed to receive initial message: %v", err)
}
if msg.Setup == nil {
return fmt.Errorf("first message should always be setup")
}
if impl, ok := b.impl.(ExecTaskStreamingRawDriver); ok {
return impl.ExecTaskStreamingRaw(server.Context(),
msg.Setup.TaskId, msg.Setup.Command, msg.Setup.Tty,
server)
}
d, ok := b.impl.(ExecTaskStreamingDriver)
if !ok {
return fmt.Errorf("driver does not support exec")
}
execOpts, errCh := StreamToExecOptions(server.Context(),
msg.Setup.Command, msg.Setup.Tty,
server)
result, err := d.ExecTaskStreaming(server.Context(),View on GitHub (pinned to 482b49bf1a)
Solutions
- Fix the client to send ExecTaskStreamingRequest{Setup: &proto.ExecTaskStreamingRequest_Setup{...}} as the first frame.
- Update the driver plugin and Nomad client together so both sides agree on the stream handshake.
- Check for a reconnect path in the client that resumes the stream mid-session without resending Setup.
Example fix
// before (client)
stream.Send(&proto.ExecTaskStreamingRequest{Input: firstInput})
// after (client)
stream.Send(&proto.ExecTaskStreamingRequest{Setup: &proto.ExecTaskStreamingRequest_Setup{
TaskId: taskID, Command: cmd, Tty: tty,
}})
stream.Send(&proto.ExecTaskStreamingRequest{Input: firstInput}) Defensive patterns
Strategy: validation
Validate before calling
// Client-side: assert first frame carries Setup before Send
func firstFrameValid(req *proto.ExecTaskStreamingRequest) bool {
return req != nil && req.GetSetup() != nil && req.GetSetup().TaskId != ""
} Type guard
func hasSetup(req *proto.ExecTaskStreamingRequest) bool {
return req != nil && req.GetSetup() != nil
} Try / catch
if err := streamErr; err != nil && strings.Contains(err.Error(), "first message should always be setup") {
// fix client framing; do not blind-retry with the same frame order
} Prevention
- Centralize stream framing in one client helper that always sends Setup first.
- Add an integration test asserting Setup is the first frame.
- Keep client and plugin protocol versions in lockstep.
- Never reuse a mid-session stream after reconnect without resending Setup.
When it happens
Trigger: A client sends an ExecTaskStreamingRequest whose first message contains only input/resize/error frames (Setup left nil) instead of the mandatory setup frame.
Common situations: Hand-rolled or out-of-date exec client implementations that forget the setup frame; protocol version drift where a newer client sends a different first-frame layout; client bug that reorders frames after a reconnect.
Related errors
- failed to receive initial message: %v
- first message should always be setup
- failed to receive initial message: %v
- task driver does not support exec
- failed to attach to exec: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/1365b738705c0c10.
Report an issue: GitHub.