Repository: osbuild/osbuild-composer
Pull request #2875 (2022-08-02 08:43:49)

tests: Workaround for mkksiso options coming from newer lorax RPM
This pull request includes:

- [ ] adequate testing for the new functionality or fixed issue
- [ ] adequate documentation informing people about the change such as
  - [ ] submit a PR for the [guides](https://github.com/osbuild/guides) repository if this PR changed any behavior described there: https://www.osbuild.org/guides/

<!--
Thanks for proposing a change to osbuild-composer!

Please don't remove the above check list. These are things that each pull
request must have before it is merged. It helps maintainers to not forget
anything.

If the reason for ticking any of the boxes is ambiguous, please add a short
note explaining why.

In addition, if this pull request fixes a downstream issue, please refer to
test/README.md and add these additional items:

- [ ] 1st commit of any `rhbz#` related PR contains bug reproducer; CI reports FAIL or
- [ ] PR contains automated tests for new functionality and
- [ ] QE has approved reproducer/new tests and
- [ ] Subsequent commits provide bug fixes without modifying the reproducer; CI reports PASS and
- [ ] QE approves this PR; RHBZ status is set to `MODIFIED + Verified=Tested`

Information regarding our GitLab pipeline (Schutzbot):

CI will not be ran automatically if WIP label is applied or the PR is in DRAFT state, instead only
a link will be provided to the pipeline which can then be triggered manually if desired. To run the
CI automatically either switch the PR to ready or apply WIP+test label.

Outside contributors need manual approval from one of the osbuild-composer maintainers.

Schutzbot will only be triggered if all Tests jobs in GitHub workflow succeed.
-->


jrusz: Ugh, there is a newer lorax in Fedora and it doesn't have the new `--ks` option... I guess the patch was only released to el9 branch? https://gitlab.com/redhat/services/products/image-builder/ci/osbuild-composer/-/jobs/2801244119#L4095

I guess add something like `&& "$ID" == "rhel"` to the if condition and come back to if it causes problems in the future?
teg: @bcl is this still necessary? Will a fixed version of mkksiso land in RHEL9? (By fixed I mean backwards compatible).

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),
 		},
 	}
 
diff --git a/test/cases/installers.sh b/test/cases/installers.sh
index 3006a86d2..9d8844edf 100755
--- a/test/cases/installers.sh
+++ b/test/cases/installers.sh
@@ -13,6 +13,7 @@ set -euo pipefail
 
 # Get OS data.
 source /usr/libexec/osbuild-composer-test/set-env-variables.sh
+source /usr/libexec/tests/osbuild-composer/shared_lib.sh
 
 # Colorful output.
 function greenprint {
@@ -64,7 +65,11 @@ echo -e 'admin\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
 EOFKS
 
     echo "Writing new ISO"
-    sudo mkksiso -c "console=ttyS0,115200" "${newksfile}" "${iso}" "${newiso}"
+    if nvrGreaterOrEqual "lorax" "34.9.18"; then
+        sudo mkksiso -c "console=ttyS0,115200" --ks "${newksfile}" "${iso}" "${newiso}"
+    else
+        sudo mkksiso -c "console=ttyS0,115200" "${newksfile}" "${iso}" "${newiso}"
+    fi
 
     echo "==== NEW KICKSTART FILE ===="
     cat "${newksfile}"
diff --git a/test/cases/ostree-ng.sh b/test/cases/ostree-ng.sh
index b863f6d30..2b9db8f09 100755
--- a/test/cases/ostree-ng.sh
+++ b/test/cases/ostree-ng.sh
@@ -6,6 +6,7 @@ set -euo pipefail
 
 # Get OS data.
 source /usr/libexec/osbuild-composer-test/set-env-variables.sh
+source /usr/libexec/tests/osbuild-composer/shared_lib.sh
 
 # Colorful output.
 function greenprint {
@@ -194,7 +195,11 @@ ostree remote add --no-gpg-verify --no-sign-verify ${OSTREE_OSNAME} ${PROD_REPO_
 EOFKS
 
     echo "Writing new ISO"
-    sudo mkksiso -c "console=ttyS0,115200" "${newksfile}" "${iso}" "${newiso}"
+    if [ "${ID}" != "fedora" ] && nvrGreaterOrEqual "lorax" "34.9.18"; then
+        sudo mkksiso -c "console=ttyS0,115200" --ks "${newksfile}" "${iso}" "${newiso}"
+    else
+        sudo mkksiso -c "console=ttyS0,115200" "${newksfile}" "${iso}" "${newiso}"
+    fi
 
     echo "==== NEW KICKSTART FILE ===="
     cat "${newksfile}"