{"record":{"id":"996ea65279d3efd3","repo":"vitessio/vitess","slug":"a-post-request-must-specify-action","errorCode":null,"errorMessage":"a POST request must specify action","messagePattern":"a POST request must specify action","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtctld/api.go","lineNumber":221,"sourceCode":"\t\t\t// Get the keyspace record.\n\t\t\tk, err := ts.GetKeyspace(ctx, keyspace)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\t// Pass the embedded proto directly or jsonpb will panic.\n\t\t\treturn k.Keyspace, err\n\t\t\t// Perform an action on a keyspace.\n\t\tcase \"POST\":\n\t\t\tif keyspace == \"\" {\n\t\t\t\treturn nil, errors.New(\"a POST request needs a keyspace in the URL\")\n\t\t\t}\n\t\t\tif err := r.ParseForm(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\taction := r.FormValue(\"action\")\n\t\t\tif action == \"\" {\n\t\t\t\treturn nil, errors.New(\"a POST request must specify action\")\n\t\t\t}\n\t\t\treturn actions.ApplyKeyspaceAction(ctx, action, keyspace), nil\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported HTTP method: %v\", r.Method)\n\t\t}\n\t})\n\n\thandleCollection(\"keyspace\", func(r *http.Request) (any, error) {\n\t\t// Valid requests: api/keyspace/my_ks/tablets (all shards)\n\t\t// Valid requests: api/keyspace/my_ks/tablets/-80 (specific shard)\n\t\titemPath := getItemPath(r.URL.Path)\n\t\tparts := strings.SplitN(itemPath, \"/\", 3)\n\n\t\tmalformedRequestError := fmt.Errorf(\"invalid keyspace path: %q  expected path: /keyspace/<keyspace>/tablets or /keyspace/<keyspace>/tablets/<shard>\", itemPath)\n\t\tif len(parts) < 2 {\n\t\t\treturn nil, malformedRequestError\n\t\t}\n\t\tif parts[1] != \"tablets\" {","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctld/api.go#L203-L239","documentation":"Within the keyspaces POST handler, after confirming a keyspace is present in the URL, the handler reads the 'action' form value; POSTs are strictly action-dispatch endpoints, so an empty action yields this error before ApplyKeyspaceAction is invoked.","triggerScenarios":"POST /api/keyspaces/<keyspace> with a body missing the 'action' form field, or sending an empty action= value; also sending JSON bodies the handler cannot read as form values (ParseForm sees nothing).","commonSituations":"Clients sending application/json instead of application/x-www-form-urlencoded so FormValue('action') is empty; typos in the field name (e.g. 'operation'); forgetting the body entirely.","solutions":["Add action=<name> as a form field to the POST body (application/x-www-form-urlencoded).","Use a valid keyspace action name as accepted by ApplyKeyspaceAction (e.g. 'Refresh').","Ensure the Content-Type header is form-encoded, not JSON."],"exampleFix":"// before\ncurl -X POST http://localhost:15000/api/keyspaces/commerce -H 'Content-Type: application/json' -d '{\"action\":\"Refresh\"}'\n// after\ncurl -X POST http://localhost:15000/api/keyspaces/commerce -d 'action=Refresh'","handlingStrategy":"validation","validationCode":"if action == \"\" {\n    return fmt.Errorf(\"keyspace POST requires a non-empty 'action' form field\")\n}\nform := url.Values{\"action\": {action}}","typeGuard":null,"tryCatchPattern":"resp, err := http.PostForm(u, form)\nif err != nil || strings.Contains(status, \"must specify action\") {\n    // fix: always send action=<name> as x-www-form-urlencoded\n}","preventionTips":["Always send 'action' as a form field, never JSON, for these endpoints.","Validate the action string against ApplyKeyspaceAction's accepted set."],"tags":["http-api","vtctld","action","post"],"backgroundTag":"missing-required-argument","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}