{"record":{"id":"80c898227c4d0cc6","repo":"hashicorp/nomad","slug":"job-id-contains-a-null-character","errorCode":null,"errorMessage":"Job ID contains a null character","messagePattern":"Job ID contains a null character","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":4748,"sourceCode":"\tnj.Periodic = j.Periodic.Copy()\n\tnj.Meta = maps.Clone(j.Meta)\n\tnj.ParameterizedJob = j.ParameterizedJob.Copy()\n\treturn nj\n}\n\n// Validate is used to check a job for reasonable configuration\nfunc (j *Job) Validate() error {\n\tvar mErr multierror.Error\n\n\tif j.Region == \"\" && j.Multiregion == nil {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job region\"))\n\t}\n\tif j.ID == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job ID\"))\n\t} else if strings.Contains(j.ID, \" \") {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job ID contains a space\"))\n\t} else if strings.Contains(j.ID, \"\\000\") {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job ID contains a null character\"))\n\t}\n\tif j.Name == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job name\"))\n\t} else if strings.Contains(j.Name, \"\\000\") {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job Name contains a null character\"))\n\t}\n\n\tif j.Namespace == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job must be in a namespace\"))\n\t}\n\tswitch j.Type {\n\tcase JobTypeCore, JobTypeService, JobTypeBatch, JobTypeSystem, JobTypeSysBatch:\n\tcase \"\":\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job type\"))\n\tdefault:\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Invalid job type: %q\", j.Type))\n\t}\n\tif len(j.Datacenters) == 0 && !j.IsMultiregion() {","sourceCodeStart":4730,"sourceCodeEnd":4766,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L4730-L4766","documentation":"Job.Validate() rejects Job IDs containing a NUL byte (\"\\000\"). NUL characters can corrupt string handling in downstream systems (logging, raft storage, C strings), so the validator explicitly disallows them. This check runs only after the empty-ID check passes.","triggerScenarios":"Submitting a job whose `id` contains an embedded null byte — typically via programmatic construction (j.ID = \"job\\x00suffix\") or deserialized input containing NUL; direct calls to Job.Validate() in Go.","commonSituations":"Parsing binary or legacy encodings into strings that introduce NULs; string truncation bugs from C interop leaving trailing NULs; malicious or corrupted input from untrusted sources.","solutions":["Strip NUL bytes from the ID before submission: strings.ReplaceAll(id, \"\\x00\", \"\")","Validate input at the boundary with a regex like ^[^\\x00]+$","Inspect the raw bytes of the ID (fmt.Sprintf(\"%q\", id)) to find hidden control characters"],"exampleFix":"// before\njob.ID = rawID\n// after\njob.ID = strings.ReplaceAll(rawID, \"\\x00\", \"\")","handlingStrategy":"validation","validationCode":"if strings.Contains(j.ID, \"\\x00\") {\n    return fmt.Errorf(\"job ID contains a null character\")\n}","typeGuard":"func isNulFree(s string) bool { return !strings.ContainsRune(s, '\\x00') }","tryCatchPattern":"if err := job.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"null character\") {\n        job.ID = strings.Map(func(r rune) rune { if r == 0 { return -1 }; return r }, job.ID)\n    }\n}","preventionTips":["Sanitize all externally supplied job identifiers at your API boundary","Reject control characters with a regex like ^[^\\x00\\x1f]+$","Inspect suspicious strings with %q formatting to reveal hidden bytes"],"tags":["nomad","go","validation","control-characters"],"backgroundTag":"invalid-identifier-format","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"}