{"record":{"id":"691488dd14ab7a0f","repo":"hashicorp/nomad","slug":"failed-to-parse-source-url-q-v","errorCode":null,"errorMessage":"failed to parse source URL %q: %v","messagePattern":"failed to parse source URL %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocrunner/taskrunner/getter/util.go","lineNumber":49,"sourceCode":")\n\nvar ErrSandboxEscape = errors.New(\"artifact includes symlink that resolves outside of sandbox\")\n\nfunc getURL(taskEnv interfaces.EnvReplacer, artifact *structs.TaskArtifact) (string, error) {\n\tsource := taskEnv.ReplaceEnv(artifact.GetterSource)\n\n\t// fixup GitHub SSH URL such as git@github.com:hashicorp/nomad.git\n\tgitSSH := false\n\tif strings.HasPrefix(source, githubPrefixSSH) {\n\t\tgitSSH = true\n\t\tsource = source[len(githubPrefixSSH):]\n\t}\n\n\tu, err := url.Parse(source)\n\tif err != nil {\n\t\treturn \"\", &Error{\n\t\t\tURL:         artifact.GetterSource,\n\t\t\tErr:         fmt.Errorf(\"failed to parse source URL %q: %v\", artifact.GetterSource, err),\n\t\t\tRecoverable: false,\n\t\t}\n\t}\n\n\t// build the URL by substituting as necessary\n\tq := u.Query()\n\tfor k, v := range artifact.GetterOptions {\n\t\tq.Set(k, taskEnv.ReplaceEnv(v))\n\t}\n\tu.RawQuery = q.Encode()\n\n\t// add the prefix back if necessary\n\tsourceURL := u.String()\n\tif gitSSH {\n\t\tsourceURL = fmt.Sprintf(\"%s%s\", githubPrefixSSH, sourceURL)\n\t}\n\n\treturn sourceURL, nil","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/taskrunner/getter/util.go#L31-L67","documentation":"getURL parses the artifact's GetterSource with url.Parse; when parsing fails (err != nil) it wraps the underlying error in a non-recoverable *Error carrying the artifact URL. Nomad throws this because the artifact fetcher cannot even construct a request if the source URL is malformed — the getter subprocess can never be launched. Recoverable is false so the artifact download will not be retried.","triggerScenarios":"url.Parse returns an error for the TaskArtifact.GetterSource — e.g. an unescaped control character or invalid percent-encoding in the source string (url.Parse rejects raw non-ASCII control bytes; note most syntactically odd strings still parse, so this fires mainly on control chars/bad escapes)","commonSituations":"Paste-in artifact sources with stray whitespace/newlines or CR characters from copy-paste, shell interpolation inserting control characters, mangled YAML/HCL quoting, or templated sources where a variable expanded to a string containing invalid byte sequences","solutions":["Print and inspect the exact artifact source value in the job file for hidden control characters or bad percent-escapes","Rewrite the source as a clean, properly quoted URL string (e.g. quote the value in HCL: source = \"https://...\")","If the source is templated, validate the expanded value (e.g. printf %q | URL-check) before submitting the job","Use consul template or nomad job inspect to see the rendered job and confirm the final source URL"],"exampleFix":"// before\nartifact {\n  source = \"https://example.com/artifact\nv1.tar.gz\"\n}\n// after\nartifact {\n  source = \"https://example.com/artifact-v1.tar.gz\"\n}","handlingStrategy":"validation","validationCode":"// validate artifact source before submitting the job\nu, err := url.Parse(src)\nif err != nil || src == \"\" {\n    return fmt.Errorf(\"invalid artifact source %q: %v\", src, err)\n}","typeGuard":null,"tryCatchPattern":"// on the client, this error is non-recoverable; catch in task-runner wrapper\nif errors.Is(err, getterErrType) && !err.Recoverable {\n    // do not retry; fix job spec\n}","preventionTips":["Quote artifact sources in HCL and avoid multi-line paste-ins","Lint job files (nomad job validate) before submission","Check rendered/templated sources with nomad job inspect","Strip control characters from templated URLs"],"tags":["url-parsing","artifacts","config"],"backgroundTag":"malformed-url","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"}