{"record":{"id":"3dc5f1c3ba8f5d63","repo":"hashicorp/nomad","slug":"error-parsing-fingerprint-output-as-json-w","errorCode":null,"errorMessage":"error parsing fingerprint output as json: %w","messagePattern":"error parsing fingerprint output as json: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/hostvolumemanager/host_volume_plugin.go","lineNumber":299,"sourceCode":"// Must complete within 5 seconds\nfunc (p *HostVolumePluginExternal) Fingerprint(ctx context.Context) (*PluginFingerprint, error) {\n\tctx, cancel := context.WithTimeout(ctx, 5*time.Second)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, p.Executable, \"fingerprint\")\n\tcmd.Env = []string{EnvOperation + \"=fingerprint\"}\n\tstdout, stderr, err := runCommand(cmd)\n\tlog := p.log.With(\n\t\t\"operation\", \"fingerprint\",\n\t\t\"stdout\", string(stdout),\n\t\t\"stderr\", string(stderr),\n\t)\n\tif err != nil {\n\t\tlog.Error(\"error running plugin\", \"error\", err)\n\t\treturn nil, fmt.Errorf(\"error fingerprinting plugin %q: %w\", p.ID, err)\n\t}\n\tfprint := &PluginFingerprint{}\n\tif err := json.Unmarshal(stdout, fprint); err != nil {\n\t\terr = fmt.Errorf(\"error parsing fingerprint output as json: %w\", err)\n\t\tlog.Error(\"error parsing plugin output\", \"error\", err)\n\t\treturn nil, err\n\t}\n\treturn fprint, nil\n}\n\n// Create calls the executable with the following parameters:\n// arguments: $1=create\n// environment:\n// - DHV_OPERATION=create\n// - DHV_VOLUMES_DIR={directory to put the volume in}\n// - DHV_PLUGIN_DIR={path to directory containing plugins}\n// - DHV_NAMESPACE={volume namespace}\n// - DHV_VOLUME_NAME={name from the volume specification}\n// - DHV_VOLUME_ID={volume ID generated by Nomad}\n// - DHV_NODE_ID={Nomad node ID}\n// - DHV_NODE_POOL={Nomad node pool}\n// - DHV_CAPACITY_MIN_BYTES={capacity_min from the volume spec, expressed in bytes}","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/hostvolumemanager/host_volume_plugin.go#L281-L317","documentation":"After the fingerprint command runs successfully, Fingerprint unmarshals the plugin's stdout JSON into a PluginFingerprint struct. If the output is not valid JSON (or not shaped as expected), Nomad wraps the json.Unmarshal error with this message, logs it, and returns it. It indicates the external plugin violated the fingerprint output contract rather than an infrastructure failure.","triggerScenarios":"Calling Fingerprint where the plugin printed non-JSON output: human-readable log lines on stdout, empty output, exit-message strings, or JSON fields whose types don't match PluginFingerprint (e.g. string where a number is expected).","commonSituations":"Plugin writes debug/logging output to stdout instead of stderr, contaminating the JSON; plugin returns empty output on early exit; plugin emits JSON with a BOM or trailing text; a plugin version emits a schema the Nomad client can't unmarshal.","solutions":["Run the plugin manually with fingerprint and inspect stdout; ensure it prints exactly one valid JSON document and sends all logs to stderr.","Validate the JSON structure against the PluginFingerprint schema (expected fields and types) and fix the plugin's output.","Check for empty stdout — usually the plugin crashed before printing; address the underlying crash first.","Upgrade or downgrade the plugin binary to a version compatible with the Nomad client's expected fingerprint schema."],"exampleFix":"// before: plugin contaminates stdout\nfmt.Println(\"starting fingerprint...\")\nfmt.Println(`{\"operations\": {\"create\": true}}`)\n\n// after\nfmt.Fprintln(os.Stderr, \"starting fingerprint...\")\nfmt.Println(`{\"operations\": {\"create\": true}}`)","handlingStrategy":"validation","validationCode":"func validateFingerprintOutput(stdout []byte) error {\n    trimmed := bytes.TrimPrefix(bytes.TrimSpace(stdout), []byte(\"\\xef\\xbb\\xbf\")) // strip BOM\n    if len(trimmed) == 0 {\n        return fmt.Errorf(\"plugin produced empty fingerprint output\")\n    }\n    var fprint PluginFingerprint\n    if err := json.Unmarshal(trimmed, &fprint); err != nil {\n        return fmt.Errorf(\"plugin stdout is not valid fingerprint JSON: %w; output: %.200q\", err, trimmed)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"error parsing fingerprint output as json\") {\n    var jsonErr *json.UnmarshalTypeError\n    if errors.As(err, &jsonErr) {\n        log.Error(\"fingerprint field type mismatch\", \"field\", jsonErr.Field)\n    } else {\n        log.Error(\"plugin stdout contaminated; ensure logs go to stderr\", \"err\", err)\n    }\n}","preventionTips":["In plugins, write all logs/diagnostics to stderr; stdout must carry only the final JSON.","Print exactly one JSON document with no BOM or trailing text.","Schema-test plugin fingerprint output against the PluginFingerprint struct in CI.","Pin plugin versions compatible with the Nomad client's expected schema."],"tags":["plugin","json","fingerprint","parsing","host-volumes","go"],"backgroundTag":"invalid-json-output","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"}