{"record":{"id":"49d0088b9c41b7ad","repo":"hashicorp/nomad","slug":"failed-to-marshal-command-into-json-v","errorCode":null,"errorMessage":"failed to marshal command into json: %v","messagePattern":"failed to marshal command into json: (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"command/agent/alloc_endpoint.go","lineNumber":597,"sourceCode":"\n\tif rpcErr != nil {\n\t\tif structs.IsErrNoNodeConn(rpcErr) || structs.IsErrUnknownAllocation(rpcErr) || structs.IsErrUnknownNode(rpcErr) {\n\t\t\trpcErr = CodedError(404, rpcErr.Error())\n\t\t}\n\t}\n\n\treturn reply.Results, rpcErr\n}\n\nfunc (s *HTTPServer) allocExec(allocID string, resp http.ResponseWriter, req *http.Request) (any, error) {\n\t// Build the request and parse the ACL token\n\ttask := req.URL.Query().Get(\"task\")\n\tcmdJsonStr := req.URL.Query().Get(\"command\")\n\tvar command []string\n\terr := json.Unmarshal([]byte(cmdJsonStr), &command)\n\tif err != nil {\n\t\t// this shouldn't happen, []string is always be serializable to json\n\t\treturn nil, fmt.Errorf(\"failed to marshal command into json: %v\", err)\n\t}\n\n\tttyB := false\n\tif tty := req.URL.Query().Get(\"tty\"); tty != \"\" {\n\t\tttyB, err = strconv.ParseBool(tty)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"tty value is not a boolean: %v\", err)\n\t\t}\n\t}\n\n\targs := cstructs.AllocExecRequest{\n\t\tAllocID: allocID,\n\t\tTask:    task,\n\t\tCmd:     command,\n\t\tTty:     ttyB,\n\t}\n\ts.parse(resp, req, &args.QueryOptions.Region, &args.QueryOptions)\n","sourceCodeStart":579,"sourceCodeEnd":615,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/alloc_endpoint.go#L579-L615","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: unencoded command\ncurl 'http://localhost:4646/v1/client/allocation/abc/exec?command=ls+-la&task=web'\n// after: JSON-encoded string array\ncurl 'http://localhost:4646/v1/client/allocation/abc/exec?command=%5B%22ls%22%2C%22-la%22%5D&task=web'","handlingStrategy":"validation","validationCode":"cmdJSON, err := json.Marshal([]string{\"ls\", \"-la\"})\nif err != nil { return err }\nu := fmt.Sprintf(\".../exec?allocID=%s&task=%s&command=%s\",\n    url.QueryEscape(allocID), url.QueryEscape(task), url.QueryEscape(string(cmdJSON)))","typeGuard":"func isStringArray(v []interface{}) ([]string, bool) {\n    out := make([]string, 0, len(v))\n    for _, e := range v {\n        s, ok := e.(string)\n        if !ok { return nil, false }\n        out = append(out, s)\n    }\n    return out, true\n}","tryCatchPattern":"var command []string\nif err := json.Unmarshal([]byte(cmdJSON), &command); err != nil {\n    return fmt.Errorf(\"command must be a JSON array of strings: %w\", err)\n}","preventionTips":["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."],"tags":["nomad","http-api","json","exec","request-validation"],"backgroundTag":"malformed-json-parameter","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}