{"record":{"id":"767eff7c9f6a244c","repo":"hashicorp/nomad","slug":"failed-to-convert-hcl-schema","errorCode":null,"errorMessage":"failed to convert HCL schema: ","messagePattern":"failed to convert HCL schema: ","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"plugins/shared/cmd/launcher/command/device.go","lineNumber":183,"sourceCode":"\tdev := raw.(device.DevicePlugin)\n\treturn dev, func() { client.Kill() }, nil\n}\n\nfunc (c *Device) getSpec() (hcldec.Spec, error) {\n\t// Get the schema so we can parse the config\n\tspec, err := c.dev.ConfigSchema()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get config schema: %v\", err)\n\t}\n\n\t// Convert the schema\n\tschema, diag := hclspecutils.Convert(spec)\n\tif diag.HasErrors() {\n\t\terrStr := \"failed to convert HCL schema: \"\n\t\tfor _, err := range diag.Errs() {\n\t\t\terrStr = fmt.Sprintf(\"%s\\n* %s\", errStr, err.Error())\n\t\t}\n\t\treturn nil, errors.New(errStr)\n\t}\n\n\treturn schema, nil\n}\n\nfunc (c *Device) setConfig(spec hcldec.Spec, apiVersion string, config []byte, nmdCfg *base.AgentConfig) error {\n\t// Parse the config into hcl\n\tconfigVal, err := hclConfigToAny(config)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval, diag, diagErrs := hclutils.ParseHclInterface(configVal, spec, nil)\n\tif diag.HasErrors() {\n\t\treturn multierror.Append(errors.New(\"failed to parse config: \"), diagErrs...)\n\t}\n\n\tcdata, err := msgpack.Marshal(val, val.Type())","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/plugins/shared/cmd/launcher/command/device.go#L165-L201","documentation":"In plugins/shared/cmd/launcher/command/device.go:183 (getSpec, called by Run), the device plugin launcher converts its HCL spec (hclspecutils.Convert) into a schema. If the returned diagnostics contain errors, it builds a message starting with 'failed to convert HCL schema: ' followed by each diagnostic on its own '* ' line. It means the device plugin's declared config schema itself is invalid HCL-spec — a plugin-side defect, not user config.","triggerScenarios":"Running a device plugin whose GetDevicePluginSpec/hclspec definition fails to compile into a valid HCL schema, e.g. malformed hclspec protobuf fields, unsupported spec types, or contradictory attribute definitions in the plugin binary.","commonSituations":"Developing a custom Nomad device plugin with a broken hclspec; plugin built against mismatched nomad-plugin API versions where a spec construct is no longer supported; shipping a plugin with a typo in its spec declaration.","solutions":["Fix the plugin's hclspec definition so it converts cleanly (the appended diagnostics name the offending fields)","Rebuild the plugin against the nomad plugin SDK version matching your Nomad client","As a workaround, pin to a previously working plugin binary release"],"exampleFix":"// before\nspec := hclspec.NewObject(map[string]*hclspec.Spec{\n    \"devices\": nil, // invalid: nil spec causes conversion diagnostics\n})\n// after\nspec := hclspec.NewObject(map[string]*hclspec.Spec{\n    \"devices\": hclspec.NewAttr(\"devices\", \"list(string)\", false),\n})","handlingStrategy":"fallback","validationCode":"// preflight: run the plugin's spec conversion in a test\n_, diag := hclspecutils.Convert(pluginSpec)\nif diag.HasErrors() { t.Fatalf(\"invalid plugin spec: %v\", diag.Errs()) }","typeGuard":null,"tryCatchPattern":"schema, err := getSpec(spec)\nif err != nil {\n    return fmt.Errorf(\"device plugin spec invalid: %w — rebuild/rollback plugin\", err)\n}","preventionTips":["Test plugin hclspec conversion in CI","Keep the plugin SDK version aligned with the Nomad client","Keep a known-good plugin binary for rollback"],"tags":["device-plugin","hcl","schema","launcher"],"backgroundTag":"hcl-schema-conversion-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"}