{"record":{"id":"128a91ce1c9aa669","repo":"hashicorp/nomad","slug":"cannot-upload-management-tokens","errorCode":null,"errorMessage":"cannot upload management tokens","messagePattern":"cannot upload management tokens","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/acl.go","lineNumber":153,"sourceCode":"\tvar resp ACLToken\n\twm, err := a.client.put(\"/v1/acl/token\", token, &resp, q)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn &resp, wm, nil\n}\n\n// Upload is used to create a client token with pre-specified AccessorID and\n// SecretID. Management tokens cannot be uploaded and must be created with Create.\nfunc (a *ACLTokens) Upload(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {\n\tif token.AccessorID == \"\" {\n\t\treturn nil, nil, errors.New(\"missing accessor ID\")\n\t}\n\tif token.SecretID == \"\" {\n\t\treturn nil, nil, errors.New(\"missing secret ID\")\n\t}\n\tif token.Type == \"management\" {\n\t\treturn nil, nil, errors.New(\"cannot upload management tokens\")\n\t}\n\tvar resp ACLToken\n\twm, err := a.client.put(\"/v1/acl/token/\"+token.AccessorID, token, &resp, q)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn &resp, wm, nil\n}\n\n// Update is used to update an existing token\nfunc (a *ACLTokens) Update(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {\n\tif token.AccessorID == \"\" {\n\t\treturn nil, nil, errors.New(\"missing accessor ID\")\n\t}\n\tvar resp ACLToken\n\twm, err := a.client.put(\"/v1/acl/token/\"+token.AccessorID,\n\t\ttoken, &resp, q)\n\tif err != nil {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/api/acl.go#L135-L171","documentation":"Create with topologies/capacity requires a plugin with a CSI controller (ControllerRequired), but the referenced plugin is node-only. Controller-less plugins cannot provision volumes, so Nomad rejects the create with this message.","triggerScenarios":"`nomad volume create` (Create RPC) whose volume's plugin_id points to a plugin registered only as type=\"node\" (no controller task) — e.g. hostpath or node-only CSI drivers.","commonSituations":"Registering a CSI plugin job with only node plugins, then trying dynamic provisioning; driver (e.g. some local/path provisioners) simply has no controller; typo'd plugin ID resolving to a node-only plugin.","solutions":["Add a controller plugin task to the plugin job and re-register (nomad job stop/start) so ControllerRequired is satisfied","Use `nomad volume register` (static registration) instead of dynamic create for controller-less drivers","Verify with nomad plugin status <id> that Controllers > 0"],"exampleFix":"// before: node-only CSI plugin\nplugin \"hostpath\" {\n  type = \"node\"\n}\n// after: dynamic provisioning needs a controller too\nplugin \"hostpath\" {\n  type = \"controller\"\n}\nplugin \"hostpath\" {\n  type = \"node\"\n}","handlingStrategy":"validation","validationCode":"plug, _, _ := client.CSIPlugins().Get(pluginID, nil)\nif plug == nil || plug.ControllersExpected == 0 {\n    // controller-less driver: use static volume register instead of create\n}","typeGuard":null,"tryCatchPattern":"if err := create(req); err != nil && strings.Contains(err.Error(), \"plugin has no controller\") {\n    // fall back to static registration or deploy a controller plugin\n}","preventionTips":["Register both controller and node tasks for dynamic provisioning drivers","Check nomad plugin status Controllers count before create","Choose drivers that implement ControllerCreate for dynamic workflows"],"tags":["nomad","csi","controller","dynamic-provisioning"],"backgroundTag":"csi-controller-required","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"}