{"record":{"id":"01c6025f2ddd3e75","repo":"hashicorp/nomad","slug":"controller-create-volume-v","errorCode":null,"errorMessage":"controller create volume: %v","messagePattern":"controller create volume: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/client_csi_endpoint.go","lineNumber":84,"sourceCode":"\t\t\"ClientCSI.ControllerDetachVolume\",\n\t\tstructs.RateMetricWrite,\n\t\targs, reply)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"controller detach volume: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (a *ClientCSI) ControllerCreateVolume(args *cstructs.ClientCSIControllerCreateVolumeRequest, reply *cstructs.ClientCSIControllerCreateVolumeResponse) error {\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"client_csi_controller\", \"create_volume\"}, time.Now())\n\n\terr := a.sendCSIControllerRPC(args.PluginID,\n\t\t\"CSI.ControllerCreateVolume\",\n\t\t\"ClientCSI.ControllerCreateVolume\",\n\t\tstructs.RateMetricWrite,\n\t\targs, reply)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"controller create volume: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (a *ClientCSI) ControllerExpandVolume(args *cstructs.ClientCSIControllerExpandVolumeRequest, reply *cstructs.ClientCSIControllerExpandVolumeResponse) error {\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"client_csi_controller\", \"expand_volume\"}, time.Now())\n\n\terr := a.sendCSIControllerRPC(args.PluginID,\n\t\t\"CSI.ControllerExpandVolume\",\n\t\t\"ClientCSI.ControllerExpandVolume\",\n\t\tstructs.RateMetricWrite,\n\t\targs, reply)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"controller expand volume: %v\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/client_csi_endpoint.go#L66-L102","documentation":"ClientCSI.ControllerCreateVolume forwards a CSI ControllerCreateVolume RPC to the controller plugin and wraps failures as \"controller create volume: <err>\". It is invoked when Nomad registers a volume with no external ID and asks the controller to provision it.","triggerScenarios":"`nomad volume create` (dynamic provisioning) when the controller plugin is down, or the storage backend rejects creation (invalid parameters, capacity exceeded, quota, bad topology).","commonSituations":"External storage quota/capacity exhausted; invalid requested_capabilities or parameters block in the volume spec; plugin doesn't implement CREATE/DELETE controller capability; topology/zone mismatch (requested zone the backend can't serve).","solutions":["Check `nomad plugin status <plugin-id>` for a healthy controller; restart the plugin task if down.","Inspect plugin alloc logs for the backend CSI error (quota, invalid param, capacity) and fix the storage side or the volume's parameters/topologies in the HCL.","Verify the plugin advertises ControllerCreateVolume; if not, pre-provision the volume externally and register it with a volume ID instead of dynamic creation."],"exampleFix":"# before\nvolume \"db\" {\n  plugin_id   = \"ebs-plugin\"\n  capacity_min = \"10GiB\"\n  capacity_max = \"10GiB\"\n  topology_request { required = true\n    topology { segments = { \"topology.ebs.amazonaws.com/zone\" = \"us-east-1f\" } } }\n}\n# after: capacity raised / zone fixed to one the backend supports\nvolume \"db\" {\n  plugin_id   = \"ebs-plugin\"\n  capacity_min = \"20GiB\"\n  capacity_max = \"40GiB\"\n  topology_request { required = true\n    topology { segments = { \"topology.ebs.amazonaws.com/zone\" = \"us-east-1a\" } } }\n}","handlingStrategy":"try-catch","validationCode":"const plugin = await nomad.plugin(pluginID)\nconst caps = plugin.controllerCapabilities ?? []\nif (!caps.includes('CREATE_DELETE_VOLUME')) {\n  throw new Error('plugin cannot create volumes; pre-provision and register an existing volume ID instead')\n}\n// verify backend has capacity/quota via plugin logs or external API\n","typeGuard":"const supportsCreate = (p) => (p.controllerCapabilities ?? []).includes('CREATE_DELETE_VOLUME')","tryCatchPattern":"try { return await createVolume(spec) }\ncatch (e) {\n  if (String(e).startsWith('controller create volume')) {\n    log.error('dynamic provisioning failed:', e)\n    return provisionExternallyAndRegister(spec) // fallback path\n  }\n  throw e\n}","preventionTips":["Verify the controller advertises CREATE_DELETE_VOLUME before relying on dynamic provisioning.","Keep storage quota headroom; watch backend capacity alerts.","Validate topology/zone segments against the provider's real zones.","Start with capacity_min == capacity_max for predictable provisioning.","Test volume HCL in staging before production provisioning."],"tags":["nomad","csi","storage","provisioning","plugin"],"backgroundTag":"csi-controller-rpc-failed","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"}