hashicorp/nomad · error
failed to marshal command into json: %v
Error message
failed to marshal command into json: %v
What it means
This error comes from allocExec in command/agent/alloc_endpoint.go:597 when the `command` query parameter of the exec HTTP endpoint fails json.Unmarshal into a []string. The comment in the code notes this 'shouldn't happen' for well-formed JSON, so it fires when the caller passes a command parameter that is not a JSON array of strings (e.g. a bare word, malformed JSON, or an array with non-string elements).
Source
Thrown at command/agent/alloc_endpoint.go:597
if rpcErr != nil {
if structs.IsErrNoNodeConn(rpcErr) || structs.IsErrUnknownAllocation(rpcErr) || structs.IsErrUnknownNode(rpcErr) {
rpcErr = CodedError(404, rpcErr.Error())
}
}
return reply.Results, rpcErr
}
func (s *HTTPServer) allocExec(allocID string, resp http.ResponseWriter, req *http.Request) (any, error) {
// Build the request and parse the ACL token
task := req.URL.Query().Get("task")
cmdJsonStr := req.URL.Query().Get("command")
var command []string
err := json.Unmarshal([]byte(cmdJsonStr), &command)
if err != nil {
// this shouldn't happen, []string is always be serializable to json
return nil, fmt.Errorf("failed to marshal command into json: %v", err)
}
ttyB := false
if tty := req.URL.Query().Get("tty"); tty != "" {
ttyB, err = strconv.ParseBool(tty)
if err != nil {
return nil, fmt.Errorf("tty value is not a boolean: %v", err)
}
}
args := cstructs.AllocExecRequest{
AllocID: allocID,
Task: task,
Cmd: command,
Tty: ttyB,
}
s.parse(resp, req, &args.QueryOptions.Region, &args.QueryOptions)
View on GitHub (pinned to 482b49bf1a)
Solutions
- JSON-encode the command as an array of strings in the query parameter: command=%5B%22ls%22%2C%22-la%22%5D (i.e. ["ls","-la"]).
- Use the official Nomad CLI (`nomad alloc exec`) or API client, which encodes the parameter correctly, instead of hand-building URLs.
- Validate the JSON with a quick marshal/unmarshal locally (e.g. `echo '["ls"]' | jq .`) before sending the request.
- Check that the shell/curl quoting preserves the double quotes in the JSON array.
Example fix
// before: unencoded command curl 'http://localhost:4646/v1/client/allocation/abc/exec?command=ls+-la&task=web' // after: JSON-encoded string array curl 'http://localhost:4646/v1/client/allocation/abc/exec?command=%5B%22ls%22%2C%22-la%22%5D&task=web'
Defensive patterns
Strategy: validation
Validate before calling
cmdJSON, err := json.Marshal([]string{"ls", "-la"})
if err != nil { return err }
u := fmt.Sprintf(".../exec?allocID=%s&task=%s&command=%s",
url.QueryEscape(allocID), url.QueryEscape(task), url.QueryEscape(string(cmdJSON))) Type guard
func isStringArray(v []interface{}) ([]string, bool) {
out := make([]string, 0, len(v))
for _, e := range v {
s, ok := e.(string)
if !ok { return nil, false }
out = append(out, s)
}
return out, true
} Try / catch
var command []string
if err := json.Unmarshal([]byte(cmdJSON), &command); err != nil {
return fmt.Errorf("command must be a JSON array of strings: %w", err)
} Prevention
- Always build the command parameter with json.Marshal plus url.QueryEscape, never string concatenation.
- Use the official Nomad API client or CLI instead of hand-built URLs.
- Validate the JSON shape (array of strings) client-side before sending.
- Beware shell quoting stripping double quotes around the JSON array.
When it happens
Trigger: Calling GET /v1/client/allocation/<allocID>/exec with a `command` query parameter that is not valid JSON or not a JSON array of strings — e.g. command=ls (no JSON quoting), command=["ls"-x], command=[1,2], or a URL-encoded value whose quotes were stripped by an intermediary.
Common situations: Hand-constructing the exec URL without JSON-encoding the command; shell/quoting issues where double quotes around the JSON array are lost; older custom tooling sending a space-separated command string instead of a JSON array.
Related errors
- tty value is not a boolean: %v
- Request body is empty
- no exec command is configured
- Missing allocation ID
- Filter expression cannot be used with other filter parameter
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/49d0088b9c41b7ad.
Report an issue: GitHub.