{"record":{"id":"a48747f0e3a7b9b8","repo":"argoproj/argo-workflows","slug":"artifact-key-q-must-not-contain-empty-segments","errorCode":null,"errorMessage":"artifact key %q must not contain empty segments","messagePattern":"artifact key %q must not contain empty segments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/artifactkey.go","lineNumber":40,"sourceCode":"\tif !strings.HasPrefix(key, prefix) {\n\t\treturn fmt.Errorf(\"artifact key %q must start with %q\", key, prefix)\n\t}\n\tif strings.Contains(key, \"..\") {\n\t\treturn fmt.Errorf(\"artifact key %q must not contain '..'\", key)\n\t}\n\tif strings.HasPrefix(key, \"/\") {\n\t\treturn fmt.Errorf(\"artifact key %q must not be an absolute path\", key)\n\t}\n\tif path.Clean(key) != key {\n\t\treturn fmt.Errorf(\"artifact key %q is not in canonical form\", key)\n\t}\n\n\tparts := strings.Split(key, \"/\")\n\tif len(parts) != 4 {\n\t\treturn fmt.Errorf(\"artifact key %q must have exactly 4 segments: uploads/{namespace}/{uuid}/{filename}\", key)\n\t}\n\tif slices.Contains(parts, \"\") {\n\t\treturn fmt.Errorf(\"artifact key %q must not contain empty segments\", key)\n\t}\n\n\tuuidSegment := parts[2]\n\tif _, err := uuid.Parse(uuidSegment); err != nil {\n\t\treturn fmt.Errorf(\"artifact key %q must have a valid UUID segment: %w\", key, err)\n\t}\n\n\tfilename := parts[3]\n\tif path.Base(filename) != filename {\n\t\treturn fmt.Errorf(\"artifact key %q must have a bare filename segment\", key)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":22,"sourceCodeEnd":55,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/server/utils/artifactkey.go#L22-L55","documentation":"None of the 4 segments may be empty. Empty segments typically arise from duplicate slashes or a missing uuid/filename; because canonical form is also enforced, this catches edge cases like keys ending in '/' or composed with empty interpolated values.","triggerScenarios":"Keys like 'uploads/ns//file' (empty uuid segment), 'uploads/ns/uuid/' (trailing slash, empty filename), or empty variables interpolated into the key ('' namespace or filename).","commonSituations":"Unset UUID or filename variables in templates; string building with empty env vars; trailing slash from URL parsing.","solutions":["Ensure every interpolated value (namespace, uuid, filename) is non-empty before composing the key.","Generate a uuid.NewString() for the third segment and validate the filename is non-empty and a bare base name.","Rebuild the key with path.Join so empty segments are impossible."],"exampleFix":"// before\nkey := fmt.Sprintf(\"uploads/%s/%s/%s\", ns, id, \"\") // empty filename\n// after\nfilename := \"app.log\"\nif filename == \"\" || id == \"\" { return errors.New(\"missing key components\") }\nkey := fmt.Sprintf(\"uploads/%s/%s/%s\", ns, id, filename)","handlingStrategy":"validation","validationCode":"parts := strings.Split(key, \"/\")\nfor _, p := range parts {\n\tif p == \"\" { return errors.New(\"key contains empty segments\") }\n}","typeGuard":"func noEmptySegments(key string) bool {\n\treturn !slices.Contains(strings.Split(key, \"/\"), \"\")\n}","tryCatchPattern":"if err := utils.ValidateUploadedArtifactKey(ns, key); err != nil {\n\tif strings.Contains(err.Error(), \"empty segments\") {\n\t\t// regenerate key: some component was empty\n\t\tkey = path.Join(\"uploads\", ns, uuid.NewString(), defaultFilename)\n\t}\n}","preventionTips":["Check all interpolated values (namespace, uuid, filename) are non-empty before composing keys.","Use path.Join so empty parts never create empty segments.","Fail fast when required key components (e.g. job UUID) are missing instead of emitting an empty string."],"tags":["artifacts","validation"],"backgroundTag":"artifact-key-validation","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}