Repository: osbuild/osbuild-composer
Pull request #2876 (2022-08-02 10:24:37)

clienterrors: Remove ellipsis operator
The alternative is keeping the ellipsis operator, but always only adding the 1st element as details to the job error.


Patch:
diff --git a/cmd/osbuild-worker/jobimpl-container-resolve.go b/cmd/osbuild-worker/jobimpl-container-resolve.go
index a9be8b231..ebeaff9d9 100644
--- a/cmd/osbuild-worker/jobimpl-container-resolve.go
+++ b/cmd/osbuild-worker/jobimpl-container-resolve.go
@@ -38,7 +38,7 @@ func (impl *ContainerResolveJobImpl) Run(job worker.Job) error {
 	specs, err := resolver.Finish()
 
 	if err != nil {
-		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorContainerResolution, err.Error())
+		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorContainerResolution, err.Error(), nil)
 	} else {
 		for i, spec := range specs {
 			result.Specs[i] = worker.ContainerSpec{
diff --git a/cmd/osbuild-worker/jobimpl-depsolve.go b/cmd/osbuild-worker/jobimpl-depsolve.go
index 718692b21..8ec1ba6aa 100644
--- a/cmd/osbuild-worker/jobimpl-depsolve.go
+++ b/cmd/osbuild-worker/jobimpl-depsolve.go
@@ -50,20 +50,20 @@ func (impl *DepsolveJobImpl) Run(job worker.Job) error {
 			// Error originates from dnf-json
 			switch e.Kind {
 			case "DepsolveError":
-				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFDepsolveError, err.Error())
+				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFDepsolveError, err.Error(), nil)
 			case "MarkingErrors":
-				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFMarkingErrors, err.Error())
+				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFMarkingErrors, err.Error(), nil)
 			case "RepoError":
-				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFRepoError, err.Error())
+				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFRepoError, err.Error(), nil)
 			default:
 				// This still has the kind/reason format but a kind that's returned
 				// by dnf-json and not explicitly handled here.
-				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFOtherError, err.Error())
+				result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFOtherError, err.Error(), nil)
 				logWithId.Errorf("Unhandled dnf-json error in depsolve job: %v", err)
 			}
 		case error:
 			// Error originates from internal/rpmmd, not from dnf-json
-			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorRPMMDError, err.Error())
+			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorRPMMDError, err.Error(), nil)
 			logWithId.Errorf("rpmmd error in depsolve job: %v", err)
 		}
 	}
diff --git a/cmd/osbuild-worker/jobimpl-koji-finalize.go b/cmd/osbuild-worker/jobimpl-koji-finalize.go
index 2a8e9b9bb..cda774e84 100644
--- a/cmd/osbuild-worker/jobimpl-koji-finalize.go
+++ b/cmd/osbuild-worker/jobimpl-koji-finalize.go
@@ -137,7 +137,7 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error {
 
 	// Check the dependencies early.
 	if hasFailedDependency(*initArgs, osbuildResults) {
-		kojiFinalizeJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFailedDependency, "At least one job dependency failed")
+		kojiFinalizeJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFailedDependency, "At least one job dependency failed", nil)
 		return nil
 	}
 
@@ -156,7 +156,7 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error {
 		kojiTargetResults := buildArgs.TargetResultsByName(target.TargetNameKoji)
 		// Only a single Koji target is allowed per osbuild job
 		if len(kojiTargetResults) != 1 {
-			kojiFinalizeJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFinalize, "Exactly one Koji target result is expected per osbuild job")
+			kojiFinalizeJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFinalize, "Exactly one Koji target result is expected per osbuild job", nil)
 			return nil
 		}
 
@@ -211,7 +211,7 @@ func (impl *KojiFinalizeJobImpl) Run(job worker.Job) error {
 	var result worker.KojiFinalizeJobResult
 	err = impl.kojiImport(args.Server, build, buildRoots, images, args.KojiDirectory, initArgs.Token)
 	if err != nil {
-		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFinalize, err.Error())
+		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiFinalize, err.Error(), nil)
 		return err
 	}
 
diff --git a/cmd/osbuild-worker/jobimpl-koji-init.go b/cmd/osbuild-worker/jobimpl-koji-init.go
index 178aaeb87..bbbf45bfd 100644
--- a/cmd/osbuild-worker/jobimpl-koji-init.go
+++ b/cmd/osbuild-worker/jobimpl-koji-init.go
@@ -57,7 +57,7 @@ func (impl *KojiInitJobImpl) Run(job worker.Job) error {
 	var result worker.KojiInitJobResult
 	result.Token, result.BuildID, err = impl.kojiInit(args.Server, args.Name, args.Version, args.Release)
 	if err != nil {
-		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiInit, err.Error())
+		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorKojiInit, err.Error(), nil)
 	}
 
 	err = job.Update(&result)
diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go
index f0007718a..2536a8b41 100644
--- a/cmd/osbuild-worker/jobimpl-osbuild.go
+++ b/cmd/osbuild-worker/jobimpl-osbuild.go
@@ -177,7 +177,7 @@ func validateResult(result *worker.OSBuildJobResult, jobID string) {
 	if result.OSBuildOutput == nil || !result.OSBuildOutput.Success {
 		reason := "osbuild job was unsuccessful"
 		logWithId.Errorf("osbuild job failed: %s", reason)
-		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, reason)
+		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, reason, nil)
 		return
 	} else {
 		logWithId.Infof("osbuild job succeeded")
@@ -195,13 +195,13 @@ func uploadToS3(a *awscloud.AWS, outputDirectory, exportPath, bucket, key, filen
 
 	_, err := a.Upload(imagePath, bucket, key)
 	if err != nil {
-		return "", clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+		return "", clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 
 	}
 
 	url, err := a.S3ObjectPresignedURL(bucket, key)
 	if err != nil {
-		return "", clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+		return "", clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 	}
 
 	return url, nil
@@ -248,7 +248,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 	hostOS, err := common.GetRedHatRelease()
 	if err != nil {
-		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, err.Error())
+		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, err.Error(), nil)
 		return nil
 	}
 	osbuildJobResult.HostOS = hostOS
@@ -298,20 +298,20 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				err = job.DynamicArgs(*jobArgs.ManifestDynArgsIdx, &manifestJR)
 			}
 			if err != nil {
-				osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, "Error parsing dynamic args")
+				osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, "Error parsing dynamic args", nil)
 				return err
 			}
 
 			// skip the job if the manifest generation failed
 			if manifestJR.JobError != nil {
-				osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorManifestDependency, "Manifest dependency failed")
+				osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorManifestDependency, "Manifest dependency failed", nil)
 				return nil
 			}
 			jobArgs.Manifest = manifestJR.Manifest
 		}
 
 		if len(jobArgs.Manifest) == 0 {
-			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyManifest, "Job has no manifest")
+			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyManifest, "Job has no manifest", nil)
 			return nil
 		}
 	}
@@ -322,12 +322,12 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 		var jobResult worker.JobResult
 		err = job.DynamicArgs(idx, &jobResult)
 		if err != nil {
-			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, "Error parsing dynamic args")
+			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, "Error parsing dynamic args", nil)
 			return err
 		}
 
 		if jobResult.JobError != nil {
-			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorJobDependency, "Job dependency failed")
+			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorJobDependency, "Job dependency failed", nil)
 			return nil
 		}
 	}
@@ -338,7 +338,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 	// get exports for all job's targets
 	exports := jobArgs.OsbuildExports()
 	if len(exports) == 0 {
-		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "no osbuild export specified for the job")
+		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "no osbuild export specified for the job", nil)
 		return nil
 	}
 
@@ -353,7 +353,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 	osbuildJobResult.OSBuildOutput, err = osbuild.RunOSBuild(jobArgs.Manifest, impl.Store, outputDirectory, exports, nil, extraEnv, true, os.Stderr)
 	// First handle the case when "running" osbuild failed
 	if err != nil {
-		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed")
+		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed", nil)
 		return err
 	}
 
@@ -381,7 +381,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 	// Second handle the case when the build failed, but osbuild finished successfully
 	if !osbuildJobResult.OSBuildOutput.Success {
-		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed")
+		osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed", nil)
 		return nil
 	}
 
@@ -394,13 +394,13 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			imagePath := path.Join(outputDirectory, jobTarget.OsbuildArtifact.ExportName, jobTarget.OsbuildArtifact.ExportFilename)
 			f, err = os.Open(imagePath)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, err.Error(), nil)
 				break
 			}
 			defer f.Close()
 			err = job.UploadArtifact(jobTarget.ImageName, f)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 
@@ -417,7 +417,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			tempDirectory, err := ioutil.TempDir(impl.Output, job.Id().String()+"-vmware-*")
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
@@ -435,13 +435,13 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			exportedImagePath := path.Join(outputDirectory, jobTarget.OsbuildArtifact.ExportName, jobTarget.OsbuildArtifact.ExportFilename)
 			err = os.Symlink(exportedImagePath, imagePath)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
 			err = vmware.UploadImage(credentials, imagePath)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 
@@ -449,7 +449,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			targetResult = target.NewAWSTargetResult(nil)
 			a, err := impl.getAWS(targetOptions.Region, targetOptions.AccessKeyID, targetOptions.SecretAccessKey, targetOptions.SessionToken)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
@@ -479,18 +479,18 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			_, err = a.Upload(imagePath, bucket, key)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 
 			ami, err := a.Register(jobTarget.ImageName, bucket, key, targetOptions.ShareWithAccounts, common.CurrentArch())
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, err.Error(), nil)
 				break
 			}
 
 			if ami == nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, "No ami returned")
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, "No ami returned", nil)
 				break
 			}
 			targetResult.Options = &target.AWSTargetResultOptions{
@@ -502,7 +502,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			targetResult = target.NewAWSS3TargetResult(nil)
 			a, bucket, err := impl.getAWSForS3Target(targetOptions)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
@@ -517,7 +517,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			targetResult = target.NewAzureTargetResult()
 			azureStorageClient, err := azure.NewStorageClient(targetOptions.StorageAccount, targetOptions.StorageAccessKey)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
@@ -537,7 +537,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			)
 
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 
@@ -547,7 +547,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			g, err := impl.getGCP(targetOptions.Credentials)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
@@ -555,7 +555,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			_, err = g.StorageObjectUpload(ctx, path.Join(outputDirectory, jobTarget.OsbuildArtifact.ExportName, jobTarget.OsbuildArtifact.ExportFilename),
 				targetOptions.Bucket, targetOptions.Object, map[string]string{gcp.MetadataKeyImageName: jobTarget.ImageName})
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 
@@ -574,7 +574,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			// check error from ComputeImageInsert()
 			if importErr != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, importErr.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, importErr.Error(), nil)
 				break
 			}
 			logWithId.Infof("[GCP] 💿 Image URL: %s", g.ComputeImageURL(jobTarget.ImageName))
@@ -583,7 +583,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				logWithId.Infof("[GCP] 🔗 Sharing the image with: %+v", targetOptions.ShareWithAccounts)
 				err = g.ComputeImageShare(ctx, jobTarget.ImageName, targetOptions.ShareWithAccounts)
 				if err != nil {
-					targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, err.Error())
+					targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, err.Error(), nil)
 					break
 				}
 			}
@@ -597,13 +597,13 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			ctx := context.Background()
 
 			if impl.AzureCreds == nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, "osbuild job has org.osbuild.azure.image target but this worker doesn't have azure credentials")
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, "osbuild job has org.osbuild.azure.image target but this worker doesn't have azure credentials", nil)
 				break
 			}
 
 			c, err := azure.NewClient(*impl.AzureCreds, targetOptions.TenantID)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, err.Error(), nil)
 				break
 			}
 			logWithId.Info("[Azure] 🔑 Logged in Azure")
@@ -620,7 +620,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				storageAccountTag,
 			)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("searching for a storage account failed: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("searching for a storage account failed: %v", err), nil)
 				break
 			}
 
@@ -638,7 +638,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 					storageAccountTag,
 				)
 				if err != nil {
-					targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("creating a new storage account failed: %v", err))
+					targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("creating a new storage account failed: %v", err), nil)
 					break
 				}
 			}
@@ -651,13 +651,13 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				storageAccount,
 			)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("retrieving the storage account key failed: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("retrieving the storage account key failed: %v", err), nil)
 				break
 			}
 
 			azureStorageClient, err := azure.NewStorageClient(storageAccount, storageAccessKey)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("creating the storage client failed: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("creating the storage client failed: %v", err), nil)
 				break
 			}
 
@@ -666,7 +666,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			logWithId.Info("[Azure] 📦 Ensuring that we have a storage container")
 			err = azureStorageClient.CreateStorageContainerIfNotExist(ctx, storageAccount, storageContainer)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("cannot create a storage container: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("cannot create a storage container: %v", err), nil)
 				break
 			}
 
@@ -698,7 +698,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				azure.DefaultUploadThreads,
 			)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, fmt.Sprintf("uploading the image failed: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, fmt.Sprintf("uploading the image failed: %v", err), nil)
 				break
 			}
 
@@ -714,7 +714,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				targetOptions.Location,
 			)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, fmt.Sprintf("registering the image failed: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, fmt.Sprintf("registering the image failed: %v", err), nil)
 				break
 			}
 			logWithId.Info("[Azure] 🎉 Image uploaded and registered!")
@@ -726,13 +726,13 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			targetResult = target.NewKojiTargetResult(nil)
 			kojiServerURL, err := url.Parse(targetOptions.Server)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("failed to parse Koji server URL: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("failed to parse Koji server URL: %v", err), nil)
 				break
 			}
 
 			kojiServer, exists := impl.KojiServers[kojiServerURL.Hostname()]
 			if !exists {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("Koji server has not been configured: %s", kojiServerURL.Hostname()))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("Koji server has not been configured: %s", kojiServerURL.Hostname()), nil)
 				break
 			}
 
@@ -740,7 +740,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			kojiAPI, err := koji.NewFromGSSAPI(targetOptions.Server, &kojiServer.creds, kojiTransport)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("failed to authenticate with Koji server %q: %v", kojiServerURL.Hostname(), err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("failed to authenticate with Koji server %q: %v", kojiServerURL.Hostname(), err), nil)
 				break
 			}
 			logWithId.Infof("[Koji] 🔑 Authenticated with %q", kojiServerURL.Hostname())
@@ -753,7 +753,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			file, err := os.Open(path.Join(outputDirectory, jobTarget.OsbuildArtifact.ExportName, jobTarget.OsbuildArtifact.ExportFilename))
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorKojiBuild, fmt.Sprintf("failed to open the image for reading: %v", err))
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorKojiBuild, fmt.Sprintf("failed to open the image for reading: %v", err), nil)
 				break
 			}
 			defer file.Close()
@@ -761,7 +761,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 			logWithId.Info("[Koji] ⬆ Uploading the image")
 			imageHash, imageSize, err := kojiAPI.Upload(file, targetOptions.UploadDirectory, jobTarget.ImageName)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 			logWithId.Info("[Koji] 🎉 Image successfully uploaded")
@@ -782,14 +782,14 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				PrivateKey:  targetOptions.PrivateKey,
 			})
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 			logWithId.Info("[OCI] 🔑 Logged in OCI")
 			logWithId.Info("[OCI] ⬆ Uploading the image")
 			file, err := os.Open(path.Join(outputDirectory, jobTarget.OsbuildArtifact.ExportName, jobTarget.OsbuildArtifact.ExportFilename))
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 			defer file.Close()
@@ -803,7 +803,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 				jobTarget.ImageName,
 			)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 			logWithId.Info("[OCI] 🎉 Image uploaded and registered!")
@@ -817,7 +817,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			client, err := impl.getContainerClient(destination, targetOptions)
 			if err != nil {
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, err.Error(), nil)
 				break
 			}
 
@@ -832,7 +832,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 
 			if err != nil {
 				logWithId.Infof("[container] 🙁 Upload of '%s' failed: %v", sourceRef, err)
-				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error())
+				targetResult.TargetError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, err.Error(), nil)
 				break
 			}
 			logWithId.Printf("[container] 🎉 Image uploaded (%s)!", digest.String())
@@ -841,7 +841,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
 		default:
 			// TODO: we may not want to return completely here with multiple targets, because then no TargetErrors will be added to the JobError details
 			// Nevertheless, all target errors will be still in the OSBuildJobResult.
-			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTarget, fmt.Sprintf("invalid target type: %s", jobTarget.Name))
+			osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidTarget, fmt.Sprintf("invalid target type: %s", jobTarget.Name), nil)
 			return nil
 		}
 
diff --git a/internal/cloudapi/v2/handler.go b/internal/cloudapi/v2/handler.go
index d73eb1409..ba878406b 100644
--- a/internal/cloudapi/v2/handler.go
+++ b/internal/cloudapi/v2/handler.go
@@ -722,11 +722,14 @@ func composeStatusErrorFromJobError(jobError *clienterrors.Error) *ComposeStatus
 	if jobError == nil {
 		return nil
 	}
-	return &ComposeStatusError{
-		Id:      int(jobError.ID),
-		Reason:  jobError.Reason,
-		Details: &jobError.Details,
+	err := &ComposeStatusError{
+		Id:     int(jobError.ID),
+		Reason: jobError.Reason,
 	}
+	if jobError.Details != nil {
+		err.Details = &jobError.Details
+	}
+	return err
 }
 
 func imageStatusFromOSBuildJobStatus(js *worker.JobStatus, result *worker.OSBuildJobResult) ImageStatusValue {
diff --git a/internal/cloudapi/v2/server.go b/internal/cloudapi/v2/server.go
index 30abf0069..a1acb9bb4 100644
--- a/internal/cloudapi/v2/server.go
+++ b/internal/cloudapi/v2/server.go
@@ -280,7 +280,7 @@ func generateManifest(ctx context.Context, workers *worker.Server, depsolveJobID
 
 	if len(dynArgs) == 0 {
 		reason := "No dynamic arguments"
-		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorNoDynamicArgs, reason)
+		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorNoDynamicArgs, reason, nil)
 		return
 	}
 
@@ -288,35 +288,35 @@ func generateManifest(ctx context.Context, workers *worker.Server, depsolveJobID
 	err = json.Unmarshal(dynArgs[0], &depsolveResults)
 	if err != nil {
 		reason := "Error parsing dynamic arguments"
-		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, reason)
+		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, reason, nil)
 		return
 	}
 
 	_, err = workers.DepsolveJobInfo(depsolveJobID, &depsolveResults)
 	if err != nil {
 		reason := "Error reading depsolve status"
-		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorReadingJobStatus, reason)
+		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorReadingJobStatus, reason, nil)
 		return
 	}
 
 	if jobErr := depsolveResults.JobError; jobErr != nil {
 		if jobErr.ID == clienterrors.ErrorDNFDepsolveError || jobErr.ID == clienterrors.ErrorDNFMarkingErrors {
-			jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDepsolveDependency, "Error in depsolve job dependency input, bad package set requested")
+			jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDepsolveDependency, "Error in depsolve job dependency input, bad package set requested", nil)
 			return
 		}
-		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDepsolveDependency, "Error in depsolve job dependency")
+		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDepsolveDependency, "Error in depsolve job dependency", nil)
 		return
 	}
 
 	if len(depsolveResults.PackageSpecs) == 0 {
-		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyPackageSpecs, "Received empty package specs")
+		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorEmptyPackageSpecs, "Received empty package specs", nil)
 		return
 	}
 
 	manifest, err := imageType.Manifest(b, options, repos, depsolveResults.PackageSpecs, nil, seed)
 	if err != nil {
 		reason := "Error generating manifest"
-		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorManifestGeneration, reason)
+		jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorManifestGeneration, reason, nil)
 		return
 	}
 
diff --git a/internal/worker/clienterrors/errors.go b/internal/worker/clienterrors/errors.go
index 4039ed361..9e6003b51 100644
--- a/internal/worker/clienterrors/errors.go
+++ b/internal/worker/clienterrors/errors.go
@@ -107,7 +107,7 @@ func (e *Error) IsDependencyError() bool {
 	}
 }
 
-func WorkerClientError(code ClientErrorCode, reason string, details ...interface{}) *Error {
+func WorkerClientError(code ClientErrorCode, reason string, details interface{}) *Error {
 	return &Error{
 		ID:      code,
 		Reason:  reason,
diff --git a/internal/worker/server.go b/internal/worker/server.go
index 30ef06fe3..20c0e76e7 100644
--- a/internal/worker/server.go
+++ b/internal/worker/server.go
@@ -110,7 +110,7 @@ func (s *Server) WatchHeartbeats() {
 			logrus.Infof("Removing unresponsive job: %s\n", id)
 
 			missingHeartbeatResult := JobResult{
-				JobError: clienterrors.WorkerClientError(clienterrors.ErrorJobMissingHeartbeat, "Worker running this job stopped responding."),
+				JobError: clienterrors.WorkerClientError(clienterrors.ErrorJobMissingHeartbeat, "Worker running this job stopped responding.", nil),
 			}
 
 			resJson, err := json.Marshal(missingHeartbeatResult)
@@ -240,7 +240,6 @@ func (s *Server) JobDependencyChainErrors(id uuid.UUID) (*clienterrors.Error, er
 		if len(depErrors) > 0 {
 			jobError.Details = depErrors
 		}
-
 		return jobError, nil
 	}
 
@@ -259,9 +258,9 @@ func (s *Server) OSBuildJobInfo(id uuid.UUID, result *OSBuildJobResult) (*JobInf
 
 	if result.JobError == nil && !jobInfo.JobStatus.Finished.IsZero() {
 		if result.OSBuildOutput == nil {
-			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed")
+			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "osbuild build failed", nil)
 		} else if len(result.OSBuildOutput.Error) > 0 {
-			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, string(result.OSBuildOutput.Error))
+			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, string(result.OSBuildOutput.Error), nil)
 		} else if len(result.TargetErrors()) > 0 {
 			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorTargetError, "at least one target failed", result.TargetErrors())
 		}
@@ -286,7 +285,7 @@ func (s *Server) KojiInitJobInfo(id uuid.UUID, result *KojiInitJobResult) (*JobI
 	}
 
 	if result.JobError == nil && result.KojiError != "" {
-		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, result.KojiError)
+		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, result.KojiError, nil)
 	}
 
 	return jobInfo, nil
@@ -303,7 +302,7 @@ func (s *Server) KojiFinalizeJobInfo(id uuid.UUID, result *KojiFinalizeJobResult
 	}
 
 	if result.JobError == nil && result.KojiError != "" {
-		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, result.KojiError)
+		result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorOldResultCompatible, result.KojiError, nil)
 	}
 
 	return jobInfo, nil
@@ -321,9 +320,9 @@ func (s *Server) DepsolveJobInfo(id uuid.UUID, result *DepsolveJobResult) (*JobI
 
 	if result.JobError == nil && result.Error != "" {
 		if result.ErrorType == DepsolveErrorType {
-			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFDepsolveError, result.Error)
+			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFDepsolveError, result.Error, nil)
 		} else {
-			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorRPMMDError, result.Error)
+			result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorRPMMDError, result.Error, nil)
 		}
 	}
 
diff --git a/templates/dashboards/grafana-dashboard-image-builder-worker-general.configmap.yml b/templates/dashboards/grafana-dashboard-image-builder-worker-general.configmap.yml
index 7c899ac0d..f144fb67a 100644
--- a/templates/dashboards/grafana-dashboard-image-builder-worker-general.configmap.yml
+++ b/templates/dashboards/grafana-dashboard-image-builder-worker-general.configmap.yml
@@ -34,8 +34,7 @@ data:
       "editable": true,
       "fiscalYearStartMonth": 0,
       "graphTooltip": 0,
-      "id": 434,
-      "iteration": 1657885396463,
+      "iteration": 1659445869463,
       "links": [],
       "liveNow": false,
       "panels": [
@@ -142,7 +141,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "1 - (\n  (\n    sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\"}[$__range]))\n    /\n    sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\"}[$__range])) \n  ) OR on() vector(0) # set a fallback if the query result is empty\n)",
+              "expr": "1 - (\n  (\n    sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$__range]))\n    /\n    sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$__range])) \n  ) OR on() vector(0) # set a fallback if the query result is empty\n)",
               "interval": "",
               "legendFormat": "",
               "refId": "A"
@@ -245,7 +244,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "(sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\"}[$interval])) OR on() vector(0))\n- \n(sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\"}[$interval])) OR on() vector(0))",
+              "expr": "(sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval])) OR on() vector(0))\n- \n(sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval])) OR on() vector(0))",
               "hide": false,
               "interval": "",
               "legendFormat": "success/sec",
@@ -257,7 +256,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "(sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\"}[$interval])) OR on() vector(0))",
+              "expr": "(sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval])) OR on() vector(0))",
               "hide": false,
               "interval": "",
               "legendFormat": "errors/sec",
@@ -346,7 +345,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "(\n  sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\"}[$interval]))\n  /\n  sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\"}[$interval]))\n)\nOR on() vector(0) # set fallback incase the above query result is empty",
+              "expr": "(\n  sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval]))\n  /\n  sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval]))\n)\nOR on() vector(0) # set fallback incase the above query result is empty",
               "interval": "",
               "legendFormat": "",
               "refId": "A"
@@ -446,7 +445,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "28 * 24 * (1 - $stability_slo)\n/ \n(\n  (\n    sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\"}[28d]))\n    / \n    sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\"}[28d]))\n  ) OR on() vector(0.01) # set fallback incase the above query result is empty\n)",
+              "expr": "28 * 24 * (1 - $stability_slo)\n/ \n(\n  (\n    sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[28d]))\n    / \n    sum(rate(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[28d]))\n  ) OR on() vector(0.01) # set fallback incase the above query result is empty\n)",
               "format": "time_series",
               "interval": "",
               "intervalFactor": 1,
@@ -549,7 +548,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "1 - (\n  (\n    1 - $stability_slo - (\n      (\n        sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\"}[28d]))\n        /\n        sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\"}[28d]))\n      ) OR on() vector(0) # set fallback for empty query result\n    ) \n  ) \n)\n/ \n(1 - $stability_slo)",
+              "expr": "1 - (\n  (\n    1 - $stability_slo - (\n      (\n        sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status=\"5xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[28d]))\n        /\n        sum(increase(image_builder_worker_total_jobs{type=~\"osbuild.*\", status!=\"4xx\", tenant=~\"$tenant\", arch=~\"$arch\"}[28d]))\n      ) OR on() vector(0) # set fallback for empty query result\n    ) \n  ) \n)\n/ \n(1 - $stability_slo)",
               "instant": false,
               "interval": "",
               "intervalFactor": 10,
@@ -662,7 +661,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "histogram_quantile(0.95, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\"}[$__range])) by (le))",
+              "expr": "histogram_quantile(0.95, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$__range])) by (le))",
               "interval": "",
               "legendFormat": "",
               "refId": "A"
@@ -800,7 +799,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "histogram_quantile(0.99, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\"}[$interval])) by (le))",
+              "expr": "histogram_quantile(0.99, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval])) by (le))",
               "hide": false,
               "interval": "",
               "legendFormat": "p99",
@@ -813,7 +812,7 @@ data:
               },
               "editorMode": "code",
               "exemplar": true,
-              "expr": "histogram_quantile(0.95, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\"}[$interval])) by (le))",
+              "expr": "histogram_quantile(0.95, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval])) by (le))",
               "hide": false,
               "interval": "",
               "legendFormat": "p95",
@@ -826,7 +825,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "histogram_quantile(0.5, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\"}[$interval])) by (le))",
+              "expr": "histogram_quantile(0.5, sum(rate(image_builder_worker_job_duration_seconds_bucket{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval])) by (le))",
               "interval": "",
               "legendFormat": "p50",
               "refId": "A"
@@ -919,7 +918,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "1 - sum(rate(image_builder_worker_job_duration_seconds_bucket{le=\"1536\",type=~\"osbuild.*\", tenant=~\"$tenant\"}[$interval]))/sum(rate(image_builder_worker_job_duration_seconds_count{type=~\"osbuild.*\", tenant=~\"$tenant\"}[$interval]))",
+              "expr": "1 - (\n    sum(rate(image_builder_worker_job_duration_seconds_bucket{le=\"1536\",type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval]))\n    /\n    sum(rate(image_builder_worker_job_duration_seconds_count{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$interval]))\n)",
               "interval": "",
               "legendFormat": "",
               "refId": "A"
@@ -967,7 +966,8 @@ data:
                 "mode": "absolute",
                 "steps": [
                   {
-                    "color": "red"
+                    "color": "red",
+                    "value": null
                   },
                   {
                     "color": "#EAB839",
@@ -1017,7 +1017,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "28 * 24 * (1 - $latency_slo) \n/ \n( \n  1.001 - ( \n    (\n      sum(rate(image_builder_worker_job_duration_seconds_bucket{le=\"1536\",type=~\"osbuild.*\", tenant=~\"$tenant\"}[$__range]))\n      /\n      sum(rate(image_builder_worker_job_duration_seconds_count{type=~\"osbuild.*\", tenant=~\"$tenant\"}[$__range]))\n    )  OR on() vector(1) # set fallback incase the above query result is empty\n  )\n)",
+              "expr": "28 * 24 * (1 - $latency_slo) \n/ \n( \n  1.001 - ( \n    (\n      sum(rate(image_builder_worker_job_duration_seconds_bucket{le=\"1536\",type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$__range]))\n      /\n      sum(rate(image_builder_worker_job_duration_seconds_count{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[$__range]))\n    )  OR on() vector(1) # set fallback incase the above query result is empty\n  )\n)",
               "format": "time_series",
               "interval": "",
               "intervalFactor": 1,
@@ -1119,7 +1119,7 @@ data:
                 "uid": "${datasource}"
               },
               "exemplar": true,
-              "expr": "1 - (\n  (\n    (\n      sum(increase(image_builder_worker_job_duration_seconds_bucket{le=\"1536\",type=~\"osbuild.*\", tenant=~\"$tenant\"}[28d]))\n      /\n      sum(increase(image_builder_worker_job_duration_seconds_count{type=~\"osbuild.*\", tenant=~\"$tenant\"}[28d])) \n    ) OR on() vector(1)  \n  ) - $latency_slo\n)\n/\n(1 - $latency_slo)",
+              "expr": "1 - (\n  (\n    (\n      sum(increase(image_builder_worker_job_duration_seconds_bucket{le=\"1536\",type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[28d]))\n      /\n      sum(increase(image_builder_worker_job_duration_seconds_count{type=~\"osbuild.*\", tenant=~\"$tenant\", arch=~\"$arch\"}[28d])) \n    ) OR on() vector(1)  \n  ) - $latency_slo\n)\n/\n(1 - $latency_slo)",
               "instant": false,
               "interval": "",
               "intervalFactor": 10,
@@ -2718,6 +2718,53 @@ data:
             "queryValue": "",
             "skipUrlSync": false,
             "type": "custom"
+          },
+          {
+            "allValue": ".*",
+            "current": {
+              "selected": true,
+              "text": [
+                "All"
+              ],
+              "value": [
+                "$__all"
+              ]
+            },
+            "hide": 0,
+            "includeAll": true,
+            "multi": true,
+            "name": "arch",
+            "options": [
+              {
+                "selected": true,
+                "text": "All",
+                "value": "$__all"
+              },
+              {
+                "selected": false,
+                "text": "x86_64",
+                "value": "x86_64"
+              },
+              {
+                "selected": false,
+                "text": "aarch64",
+                "value": "aarch64"
+              },
+              {
+                "selected": false,
+                "text": "ppc64le",
+                "value": "ppc64le"
+              },
+              {
+                "selected": false,
+                "text": "s390x",
+                "value": "s390x"
+              }
+            ],
+            "query": "x86_64, aarch64, ppc64le, s390x",
+            "queryValue": "",
+            "skipUrlSync": false,
+            "type": "custom"
           }
         ]
       },
@@ -2753,6 +2800,6 @@ data:
       "timezone": "",
       "title": "Image Builder Worker",
       "uid": "image-builder-worker",
-      "version": 8,
+      "version": 9,
       "weekStart": ""
     }


Test patch:
diff --git a/internal/cloudapi/v2/v2_koji_test.go b/internal/cloudapi/v2/v2_koji_test.go
index d7167fd7d..b63e36010 100644
--- a/internal/cloudapi/v2/v2_koji_test.go
+++ b/internal/cloudapi/v2/v2_koji_test.go
@@ -25,7 +25,7 @@ type jobResult struct {
 }
 
 func TestKojiCompose(t *testing.T) {
-	kojiServer, workerServer, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	kojiServer, workerServer, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	handler := kojiServer.Handler("/api/image-builder-composer/v2")
 	workerHandler := workerServer.Handler()
 	defer cancel()
@@ -117,7 +117,7 @@ func TestKojiCompose(t *testing.T) {
 		{
 			initResult: worker.KojiInitJobResult{
 				JobResult: worker.JobResult{
-					JobError: clienterrors.WorkerClientError(clienterrors.ErrorKojiInit, "Koji init error"),
+					JobError: clienterrors.WorkerClientError(clienterrors.ErrorKojiInit, "Koji init error", nil),
 				},
 			},
 			buildResult: worker.OSBuildJobResult{
@@ -205,7 +205,7 @@ func TestKojiCompose(t *testing.T) {
 					Success: true,
 				},
 				JobResult: worker.JobResult{
-					JobError: clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "Koji build error"),
+					JobError: clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "Koji build error", nil),
 				},
 			},
 			composeReplyCode: http.StatusCreated,
@@ -215,7 +215,6 @@ func TestKojiCompose(t *testing.T) {
 				"image_status": {
 					"status": "failure",
 					"error": {
-						"details": null,
 						"id": 10,
 						"reason": "Koji build error"
 					}
@@ -224,7 +223,6 @@ func TestKojiCompose(t *testing.T) {
 					{
 						"status": "failure",
 						"error": {
-							"details": null,
 							"id": 10,
 							"reason": "Koji build error"
 						}
@@ -299,7 +297,7 @@ func TestKojiCompose(t *testing.T) {
 			},
 			finalizeResult: worker.KojiFinalizeJobResult{
 				JobResult: worker.JobResult{
-					JobError: clienterrors.WorkerClientError(clienterrors.ErrorKojiFinalize, "Koji finalize error"),
+					JobError: clienterrors.WorkerClientError(clienterrors.ErrorKojiFinalize, "Koji finalize error", nil),
 				},
 			},
 			composeReplyCode: http.StatusCreated,
@@ -323,77 +321,8 @@ func TestKojiCompose(t *testing.T) {
 				"status": "failure"
 			}`,
 		},
-		// #7
-		{
-			initResult: worker.KojiInitJobResult{
-				BuildID: 42,
-				Token:   `"foobar"`,
-			},
-			buildResult: worker.OSBuildJobResult{
-				Arch:   test_distro.TestArchName,
-				HostOS: test_distro.TestDistroName,
-				TargetResults: []*target.TargetResult{target.NewKojiTargetResult(&target.KojiTargetResultOptions{
-					ImageMD5:  "browns",
-					ImageSize: 42,
-				})},
-				OSBuildOutput: &osbuild.Result{
-					Success: true,
-				},
-				JobResult: worker.JobResult{
-					JobError: clienterrors.WorkerClientError(
-						clienterrors.ErrorManifestDependency,
-						"Manifest dependency failed",
-						clienterrors.WorkerClientError(
-							clienterrors.ErrorDNFOtherError,
-							"DNF Error",
-						),
-					),
-				},
-			},
-			composeReplyCode: http.StatusCreated,
-			composeReply:     `{"href":"/api/image-builder-composer/v2/compose", "kind":"ComposeId"}`,
-			composeStatus: `{
-				"kind": "ComposeStatus",
-				"image_status": {
-					"error": {
-						"details": [
-							{
-								"id": 22,
-								"details": null,
-								"reason": "DNF Error"
-							}
-						],
-						"id": 9,
-						"reason": "Manifest dependency failed"
-					},
-					"status": "failure"
-				},
-				"image_statuses": [
-					{
-						"error": {
-							"details": [
-								{
-									"id": 22,
-									"details": null,
-									"reason": "DNF Error"
-								}
-							],
-							"id": 9,
-							"reason": "Manifest dependency failed"
-						},
-						"status": "failure"
-					},
-					{
-						"status": "success"
-					}
-				],
-				"koji_status": {
-					"build_id": 42
-				},
-				"status": "failure"
-			}`,
-		},
 	}
+
 	for idx, c := range cases {
 		name, version, release := "foo", "1", "2"
 		t.Run(fmt.Sprintf("Test case #%d", idx), func(t *testing.T) {
@@ -539,7 +468,7 @@ func TestKojiCompose(t *testing.T) {
 }
 
 func TestKojiJobTypeValidation(t *testing.T) {
-	server, workers, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	server, workers, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	handler := server.Handler("/api/image-builder-composer/v2")
 	defer cancel()
 
diff --git a/internal/cloudapi/v2/v2_multi_tenancy_test.go b/internal/cloudapi/v2/v2_multi_tenancy_test.go
index 65ca860c1..3ff9f7bc2 100644
--- a/internal/cloudapi/v2/v2_multi_tenancy_test.go
+++ b/internal/cloudapi/v2/v2_multi_tenancy_test.go
@@ -197,7 +197,7 @@ func runNextJob(t *testing.T, jobs []uuid.UUID, workerHandler http.Handler, orgI
 func TestMultitenancy(t *testing.T) {
 	// Passing an empty list as depsolving channels, we want to do depsolves
 	// ourselvess
-	apiServer, workerServer, q, cancel := newV2Server(t, t.TempDir(), []string{}, true)
+	apiServer, workerServer, q, cancel := newV2Server(t, t.TempDir(), []string{}, true, false)
 	handler := apiServer.Handler("/api/image-builder-composer/v2")
 	defer cancel()
 
diff --git a/internal/cloudapi/v2/v2_test.go b/internal/cloudapi/v2/v2_test.go
index 25f62716d..4b4b2e37a 100644
--- a/internal/cloudapi/v2/v2_test.go
+++ b/internal/cloudapi/v2/v2_test.go
@@ -19,12 +19,13 @@ import (
 	"github.com/osbuild/osbuild-composer/internal/osbuild"
 	"github.com/osbuild/osbuild-composer/internal/ostree/mock_ostree_repo"
 	"github.com/osbuild/osbuild-composer/internal/rpmmd"
+	"github.com/osbuild/osbuild-composer/internal/target"
 	"github.com/osbuild/osbuild-composer/internal/test"
 	"github.com/osbuild/osbuild-composer/internal/worker"
 	"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
 )
 
-func newV2Server(t *testing.T, dir string, depsolveChannels []string, enableJWT bool) (*v2.Server, *worker.Server, jobqueue.JobQueue, context.CancelFunc) {
+func newV2Server(t *testing.T, dir string, depsolveChannels []string, enableJWT bool, failDepsolve bool) (*v2.Server, *worker.Server, jobqueue.JobQueue, context.CancelFunc) {
 	q, err := fsjobqueue.New(dir)
 	require.NoError(t, err)
 	workerServer := worker.NewServer(nil, q, worker.Config{BasePath: "/api/worker/v1", JWTEnabled: enableJWT, TenantProviderFields: []string{"rh-org-id", "account_id"}})
@@ -57,7 +58,17 @@ func newV2Server(t *testing.T, dir string, depsolveChannels []string, enableJWT
 			if err != nil {
 				continue
 			}
-			rawMsg, err := json.Marshal(&worker.DepsolveJobResult{PackageSpecs: map[string][]rpmmd.PackageSpec{"build": []rpmmd.PackageSpec{rpmmd.PackageSpec{Name: "pkg1"}}}, Error: "", ErrorType: worker.ErrorType("")})
+			dJR := &worker.DepsolveJobResult{
+				PackageSpecs: map[string][]rpmmd.PackageSpec{"build": []rpmmd.PackageSpec{rpmmd.PackageSpec{Name: "pkg1"}}},
+				Error:        "",
+				ErrorType:    worker.ErrorType(""),
+			}
+
+			if failDepsolve {
+				dJR.JobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorDNFOtherError, "DNF Error", nil)
+			}
+
+			rawMsg, err := json.Marshal(dJR)
 			require.NoError(t, err)
 			err = workerServer.FinishJob(token, rawMsg)
 			if err != nil {
@@ -76,7 +87,7 @@ func newV2Server(t *testing.T, dir string, depsolveChannels []string, enableJWT
 }
 
 func TestUnknownRoute(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", "/api/image-builder-composer/v2/badroute", ``, http.StatusNotFound, `
@@ -90,7 +101,7 @@ func TestUnknownRoute(t *testing.T) {
 }
 
 func TestGetError(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", "/api/image-builder-composer/v2/errors/4", ``, http.StatusOK, `
@@ -113,7 +124,7 @@ func TestGetError(t *testing.T) {
 }
 
 func TestGetErrorList(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", "/api/image-builder-composer/v2/errors?page=3&size=1", ``, http.StatusOK, `
@@ -132,7 +143,7 @@ func TestGetErrorList(t *testing.T) {
 }
 
 func TestCompose(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	// create two ostree repos, one to serve the default test_distro ref (for fallback tests) and one to serve a custom ref
@@ -550,7 +561,7 @@ func TestCompose(t *testing.T) {
 }
 
 func TestComposeStatusSuccess(t *testing.T) {
-	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
@@ -664,7 +675,7 @@ func TestComposeStatusSuccess(t *testing.T) {
 }
 
 func TestComposeStatusFailure(t *testing.T) {
-	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
@@ -710,7 +721,6 @@ func TestComposeStatusFailure(t *testing.T) {
 		"image_status": {
 			"error": {
 				"id": 10,
-				"details": null,
 				"reason": "osbuild build failed"
 			},
 			"status": "failure"
@@ -720,7 +730,7 @@ func TestComposeStatusFailure(t *testing.T) {
 }
 
 func TestComposeStatusInvalidUUID(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", "/api/image-builder-composer/v2/composes/abcdef", ``, http.StatusBadRequest, `
@@ -736,7 +746,7 @@ func TestComposeStatusInvalidUUID(t *testing.T) {
 }
 
 func TestComposeJobError(t *testing.T) {
-	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
@@ -773,7 +783,7 @@ func TestComposeJobError(t *testing.T) {
 	}`, jobId, jobId))
 
 	jobErr := worker.JobResult{
-		JobError: clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "Error building image"),
+		JobError: clienterrors.WorkerClientError(clienterrors.ErrorBuildJob, "Error building image", nil),
 	}
 	jobResult, err := json.Marshal(worker.OSBuildJobResult{JobResult: jobErr})
 	require.NoError(t, err)
@@ -788,7 +798,6 @@ func TestComposeJobError(t *testing.T) {
 		"image_status": {
 			"error": {
 				"id": 10,
-				"details": null,
 				"reason": "Error building image"
 			},
 			"status": "failure"
@@ -798,7 +807,7 @@ func TestComposeJobError(t *testing.T) {
 }
 
 func TestComposeDependencyError(t *testing.T) {
-	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, true)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
@@ -835,9 +844,8 @@ func TestComposeDependencyError(t *testing.T) {
 	}`, jobId, jobId))
 
 	jobErr := worker.JobResult{
-		JobError: clienterrors.WorkerClientError(clienterrors.ErrorManifestDependency, "Manifest dependency failed"),
+		JobError: clienterrors.WorkerClientError(clienterrors.ErrorManifestDependency, "Manifest dependency failed", nil),
 	}
-	jobErr.JobError.Details = clienterrors.WorkerClientError(clienterrors.ErrorDNFOtherError, "DNF Error")
 	jobResult, err := json.Marshal(worker.OSBuildJobResult{JobResult: jobErr})
 	require.NoError(t, err)
 
@@ -850,11 +858,14 @@ func TestComposeDependencyError(t *testing.T) {
 		"id": "%v",
 		"image_status": {
 			"error": {
-				"details": {
-					"details": null,
-					"id": 22,
-					"reason": "DNF Error"
-				},
+				"details": [{
+					"id": 5,
+					"reason": "Error in depsolve job dependency",
+					"details": [{
+						"id": 22,
+						"reason": "DNF Error"
+					}]
+				}],
 				"id": 9,
 				"reason": "Manifest dependency failed"
 			},
@@ -864,8 +875,92 @@ func TestComposeDependencyError(t *testing.T) {
 	}`, jobId, jobId))
 }
 
+func TestComposeTargetErrors(t *testing.T) {
+	srv, wrksrv, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
+	defer cancel()
+
+	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
+	{
+		"distribution": "%s",
+		"image_request":{
+			"architecture": "%s",
+			"image_type": "aws",
+			"repositories": [{
+				"baseurl": "somerepo.org",
+				"rhsm": false
+			}],
+			"upload_options": {
+				"region": "eu-central-1"
+			}
+		 }
+	}`, test_distro.TestDistroName, test_distro.TestArch3Name), http.StatusCreated, `
+	{
+		"href": "/api/image-builder-composer/v2/compose",
+		"kind": "ComposeId"
+	}`, "id")
+
+	jobId, token, jobType, _, _, err := wrksrv.RequestJob(context.Background(), test_distro.TestArch3Name, []string{worker.JobTypeOSBuild}, []string{""})
+	require.NoError(t, err)
+	require.Equal(t, worker.JobTypeOSBuild, jobType)
+
+	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId), ``, http.StatusOK, fmt.Sprintf(`
+	{
+		"href": "/api/image-builder-composer/v2/composes/%v",
+		"kind": "ComposeStatus",
+		"id": "%v",
+		"image_status": {"status": "building"},
+		"status": "pending"
+	}`, jobId, jobId))
+
+	oJR := worker.OSBuildJobResult{
+		TargetResults: []*target.TargetResult{
+			&target.TargetResult{
+				Name:        "org.osbuild.aws",
+				Options:     target.AWSTargetResultOptions{Ami: "", Region: ""},
+				TargetError: clienterrors.WorkerClientError(clienterrors.ErrorImportingImage, "error importing image", nil),
+			},
+		},
+	}
+	jobErr := worker.JobResult{
+		JobError: clienterrors.WorkerClientError(clienterrors.ErrorTargetError, "at least one target failed", oJR.TargetErrors()),
+	}
+	oJR.JobResult = jobErr
+	jobResult, err := json.Marshal(oJR)
+	require.NoError(t, err)
+
+	err = wrksrv.FinishJob(token, jobResult)
+	require.NoError(t, err)
+	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "GET", fmt.Sprintf("/api/image-builder-composer/v2/composes/%v", jobId), ``, http.StatusOK, fmt.Sprintf(`
+	{
+		"href": "/api/image-builder-composer/v2/composes/%v",
+		"kind": "ComposeStatus",
+		"id": "%v",
+		"image_status": {
+			"error": {
+				"details": [{
+					"id": 12,
+					"reason": "error importing image",
+					"details": "org.osbuild.aws"
+				}],
+				"id": 28,
+				"reason": "at least one target failed"
+			},
+			"status": "failure",
+			"upload_status": {
+				"options": {
+					"ami": "",
+					"region": ""
+				},
+				"status": "",
+				"type": "aws"
+			}
+		},
+		"status": "failure"
+	}`, jobId, jobId))
+}
+
 func TestComposeCustomizations(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
@@ -919,7 +1014,7 @@ func TestComposeCustomizations(t *testing.T) {
 }
 
 func TestImageTypes(t *testing.T) {
-	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false)
+	srv, _, _, cancel := newV2Server(t, t.TempDir(), []string{""}, false, false)
 	defer cancel()
 
 	test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(`
diff --git a/internal/target/targetresult_test.go b/internal/target/targetresult_test.go
index e364ff9eb..dcc7a884f 100644
--- a/internal/target/targetresult_test.go
+++ b/internal/target/targetresult_test.go
@@ -85,7 +85,7 @@ func TestTargetResultUnmarshal(t *testing.T) {
 		},
 		// target results with error without options
 		{
-			resultJSON: []byte(`{"name":"org.osbuild.aws","target_error":{"id":11,"reason":"failed to uplad image","details":["detail"]}}`),
+			resultJSON: []byte(`{"name":"org.osbuild.aws","target_error":{"id":11,"reason":"failed to uplad image","details":"detail"}}`),
 			expectedResult: &TargetResult{
 				Name:        TargetNameAWS,
 				TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to uplad image", "detail"),
diff --git a/internal/weldr/compose_test.go b/internal/weldr/compose_test.go
index 44847eff7..9c4b9a937 100644
--- a/internal/weldr/compose_test.go
+++ b/internal/weldr/compose_test.go
@@ -94,7 +94,7 @@ func TestComposeStatusFromJobError(t *testing.T) {
 	require.Equal(t, jobId, j)
 
 	jobResult := worker.OSBuildJobResult{}
-	jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "Upload error")
+	jobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "Upload error", nil)
 	rawResult, err := json.Marshal(jobResult)
 	require.NoError(t, err)
 	err = api.workers.FinishJob(token, rawResult)
diff --git a/internal/worker/json_test.go b/internal/worker/json_test.go
index f3565f44f..e1c04d4a2 100644
--- a/internal/worker/json_test.go
+++ b/internal/worker/json_test.go
@@ -21,15 +21,15 @@ func TestOSBuildJobResultTargetErrors(t *testing.T) {
 				TargetResults: []*target.TargetResult{
 					{
 						Name:        target.TargetNameAWS,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameAWSS3,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", nil),
 					},
 				},
 			},
@@ -44,14 +44,14 @@ func TestOSBuildJobResultTargetErrors(t *testing.T) {
 				TargetResults: []*target.TargetResult{
 					{
 						Name:        target.TargetNameAWS,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", nil),
 					},
 					{
 						Name: target.TargetNameVMWare,
 					},
 					{
 						Name:        target.TargetNameAWSS3,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", nil),
 					},
 				},
 			},
@@ -99,19 +99,19 @@ func TestOSBuildJobResultTargetResultsByName(t *testing.T) {
 				TargetResults: []*target.TargetResult{
 					{
 						Name:        target.TargetNameAWS,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameAWSS3,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", nil),
 					},
 				},
 			},
@@ -119,7 +119,7 @@ func TestOSBuildJobResultTargetResultsByName(t *testing.T) {
 			targetResults: []*target.TargetResult{
 				{
 					Name:        target.TargetNameAWS,
-					TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
+					TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", nil),
 				},
 			},
 		},
@@ -129,19 +129,19 @@ func TestOSBuildJobResultTargetResultsByName(t *testing.T) {
 				TargetResults: []*target.TargetResult{
 					{
 						Name:        target.TargetNameAWS,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameAWSS3,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", nil),
 					},
 				},
 			},
@@ -149,11 +149,11 @@ func TestOSBuildJobResultTargetResultsByName(t *testing.T) {
 			targetResults: []*target.TargetResult{
 				{
 					Name:        target.TargetNameVMWare,
-					TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+					TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 				},
 				{
 					Name:        target.TargetNameVMWare,
-					TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+					TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 				},
 			},
 		},
@@ -163,19 +163,19 @@ func TestOSBuildJobResultTargetResultsByName(t *testing.T) {
 				TargetResults: []*target.TargetResult{
 					{
 						Name:        target.TargetNameAWS,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorInvalidTargetConfig, "can't login to AWS", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameVMWare,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "can't upload image to VMWare", nil),
 					},
 					{
 						Name:        target.TargetNameAWSS3,
-						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3"),
+						TargetError: clienterrors.WorkerClientError(clienterrors.ErrorUploadingImage, "failed to upload image to AWS S3", nil),
 					},
 				},
 			},
diff --git a/internal/worker/server_test.go b/internal/worker/server_test.go
index 7cd557db9..76fa1682a 100644
--- a/internal/worker/server_test.go
+++ b/internal/worker/server_test.go
@@ -580,7 +580,7 @@ func TestDepsolveLegacyErrorConversion(t *testing.T) {
 		Error:     reason,
 		ErrorType: errType,
 		JobResult: worker.JobResult{
-			JobError: clienterrors.WorkerClientError(clienterrors.ErrorDNFDepsolveError, reason),
+			JobError: clienterrors.WorkerClientError(clienterrors.ErrorDNFDepsolveError, reason, nil),
 		},
 	}