argoproj/argo-workflows · error
artifact key %q must have exactly 4 segments: uploads/{names
Error message
artifact key %q must have exactly 4 segments: uploads/{namespace}/{uuid}/{filename} What it means
After prefix validation, the key is split on '/' and must yield exactly 4 segments: uploads, {namespace}, {uuid}, {filename}. Fewer or more segments means the key does not match the upload endpoint's format and is rejected.
Source
Thrown at server/utils/artifactkey.go:37
// naming another user's upload under the same namespace still passes.
func ValidateUploadedArtifactKey(namespace, key string) error {
prefix := "uploads/" + namespace + "/"
if !strings.HasPrefix(key, prefix) {
return fmt.Errorf("artifact key %q must start with %q", key, prefix)
}
if strings.Contains(key, "..") {
return fmt.Errorf("artifact key %q must not contain '..'", key)
}
if strings.HasPrefix(key, "/") {
return fmt.Errorf("artifact key %q must not be an absolute path", key)
}
if path.Clean(key) != key {
return fmt.Errorf("artifact key %q is not in canonical form", key)
}
parts := strings.Split(key, "/")
if len(parts) != 4 {
return fmt.Errorf("artifact key %q must have exactly 4 segments: uploads/{namespace}/{uuid}/{filename}", key)
}
if slices.Contains(parts, "") {
return fmt.Errorf("artifact key %q must not contain empty segments", key)
}
uuidSegment := parts[2]
if _, err := uuid.Parse(uuidSegment); err != nil {
return fmt.Errorf("artifact key %q must have a valid UUID segment: %w", key, err)
}
filename := parts[3]
if path.Base(filename) != filename {
return fmt.Errorf("artifact key %q must have a bare filename segment", key)
}
return nil
}
View on GitHub (pinned to 35bff19146)
Solutions
- Flatten the path to exactly uploads/{namespace}/{uuid}/{filename} — no subdirectories.
- Generate a fresh UUID for the third segment and keep the filename as the sole final segment.
- If subdirectories are needed, use a different artifact mechanism (e.g. artifact archives/raw outputs) rather than upload keys.
Example fix
// before key := "uploads/my-ns/" + id + "/logs/2024/app.log" // after key := "uploads/my-ns/" + id + "/app.log"
Defensive patterns
Strategy: validation
Validate before calling
parts := strings.Split(key, "/")
if len(parts) != 4 || parts[0] != "uploads" {
return fmt.Errorf("key must be uploads/{namespace}/{uuid}/{filename}")
} Type guard
func hasUploadKeyShape(key string) bool { return len(strings.Split(key, "/")) == 4 } Try / catch
if err := utils.ValidateUploadedArtifactKey(ns, key); err != nil {
if strings.Contains(err.Error(), "exactly 4 segments") {
key = path.Join("uploads", ns, uuid.NewString(), filepath.Base(filename))
}
} Prevention
- Do not embed subdirectories in uploaded artifact keys — flatten filenames.
- Generate a fresh UUID per upload for the third segment.
- Unit-test key construction helpers against the 4-segment format.
When it happens
Trigger: Keys with nested subdirectories ('uploads/ns/uuid/dir/file.bin' = 5 segments), missing segments ('uploads/ns/file.bin' = 3), or keys not starting with 'uploads' that somehow passed other checks.
Common situations: Trying to store files in nested folders inside an upload; reusing keys from a different storage layout; appending query-like suffixes with slashes.
Related errors
- key unsupported: cannot get key for artifact location, becau
- artifact key %q must start with %q
- artifact key %q must not contain '..'
- artifact key %q must not be an absolute path
- artifact key %q is not in canonical form
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/f8cd6d3199ade40e.
Report an issue: GitHub.