diff --git a/site/src/api/api.ts b/site/src/api/api.ts
index 013c018d5c656..6b38515a74f1a 100644
--- a/site/src/api/api.ts
+++ b/site/src/api/api.ts
@@ -24,6 +24,7 @@ import type dayjs from "dayjs";
import userAgentParser from "ua-parser-js";
import { OneWayWebSocket } from "../utils/OneWayWebSocket";
import { delay } from "../utils/delay";
+import { type FieldError, isApiError } from "./errors";
import type {
DynamicParametersRequest,
PostWorkspaceUsageRequest,
@@ -390,6 +391,15 @@ export class MissingBuildParameters extends Error {
}
}
+export class ParameterValidationError extends Error {
+ constructor(
+ public readonly versionId: string,
+ public readonly validations: FieldError[],
+ ) {
+ super("Parameters are not valid for new template version");
+ }
+}
+
export type GetProvisionerJobsParams = {
status?: string;
limit?: number;
@@ -1239,7 +1249,6 @@ class ApiMethods {
`/api/v2/workspaces/${workspaceId}/builds`,
data,
);
-
return response.data;
};
@@ -2268,19 +2277,34 @@ class ApiMethods {
const activeVersionId = template.active_version_id;
- let templateParameters: TypesGen.TemplateVersionParameter[] = [];
-
if (isDynamicParametersEnabled) {
- templateParameters = await this.getDynamicParameters(
- activeVersionId,
- workspace.owner_id,
- oldBuildParameters,
- );
- } else {
- templateParameters =
- await this.getTemplateVersionRichParameters(activeVersionId);
+ try {
+ return await this.postWorkspaceBuild(workspace.id, {
+ transition: "start",
+ template_version_id: activeVersionId,
+ rich_parameter_values: newBuildParameters,
+ });
+ } catch (error) {
+ // If the build failed because of a parameter validation error, then we
+ // throw a special sentinel error that can be caught by the caller.
+ if (
+ isApiError(error) &&
+ error.response.status === 400 &&
+ error.response.data.validations &&
+ error.response.data.validations.length > 0
+ ) {
+ throw new ParameterValidationError(
+ activeVersionId,
+ error.response.data.validations,
+ );
+ }
+ throw error;
+ }
}
+ const templateParameters =
+ await this.getTemplateVersionRichParameters(activeVersionId);
+
const missingParameters = getMissingParameters(
oldBuildParameters,
newBuildParameters,
diff --git a/site/src/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental.tsx b/site/src/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental.tsx
index 04bb92a5e79b2..850f31185af2c 100644
--- a/site/src/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental.tsx
+++ b/site/src/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental.tsx
@@ -1,4 +1,4 @@
-import type { TemplateVersionParameter } from "api/typesGenerated";
+import type { FieldError } from "api/errors";
import { Button } from "components/Button/Button";
import {
Dialog,
@@ -14,7 +14,7 @@ import { useNavigate } from "react-router-dom";
type UpdateBuildParametersDialogExperimentalProps = {
open: boolean;
onClose: () => void;
- missedParameters: TemplateVersionParameter[];
+ validations: FieldError[];
workspaceOwnerName: string;
workspaceName: string;
templateVersionId: string | undefined;
@@ -23,7 +23,7 @@ type UpdateBuildParametersDialogExperimentalProps = {
export const UpdateBuildParametersDialogExperimental: FC<
UpdateBuildParametersDialogExperimentalProps
> = ({
- missedParameters,
+ validations,
open,
onClose,
workspaceOwnerName,
@@ -47,8 +47,8 @@ export const UpdateBuildParametersDialogExperimental: FC<
This template has{" "}
- {missedParameters.length} new parameter
- {missedParameters.length === 1 ? "" : "s"}
+ {validations.length} parameter
+ {validations.length === 1 ? "" : "s"}
{" "}
that must be configured to complete the update.
diff --git a/site/src/modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions.tsx b/site/src/modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions.tsx
index 3853af67d394f..19d12ab2a394e 100644
--- a/site/src/modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions.tsx
+++ b/site/src/modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions.tsx
@@ -1,4 +1,4 @@
-import { MissingBuildParameters } from "api/api";
+import { MissingBuildParameters, ParameterValidationError } from "api/api";
import { isApiError } from "api/errors";
import { type ApiError, getErrorMessage } from "api/errors";
import {
@@ -192,19 +192,19 @@ export const WorkspaceMoreActions: FC = ({
/>
) : (
{
changeVersionMutation.reset();
}}
workspaceOwnerName={workspace.owner_name}
workspaceName={workspace.name}
templateVersionId={
- changeVersionMutation.error instanceof MissingBuildParameters
+ changeVersionMutation.error instanceof ParameterValidationError
? changeVersionMutation.error?.versionId
: undefined
}
diff --git a/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx b/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx
index bdad9e405bd48..2fad94de2da73 100644
--- a/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx
+++ b/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx
@@ -1,4 +1,4 @@
-import { MissingBuildParameters } from "api/api";
+import { MissingBuildParameters, ParameterValidationError } from "api/api";
import { updateWorkspace } from "api/queries/workspaces";
import type {
TemplateVersion,
@@ -78,7 +78,10 @@ export const useWorkspaceUpdate = ({
updateWorkspaceMutation.reset();
},
onUpdate: (buildParameters: WorkspaceBuildParameter[]) => {
- if (updateWorkspaceMutation.error instanceof MissingBuildParameters) {
+ if (
+ updateWorkspaceMutation.error instanceof MissingBuildParameters ||
+ updateWorkspaceMutation.error instanceof ParameterValidationError
+ ) {
confirmUpdate(buildParameters);
}
},
@@ -154,8 +157,10 @@ const MissingBuildParametersDialog: FC = ({
const missedParameters =
error instanceof MissingBuildParameters ? error.parameters : [];
const versionId =
- error instanceof MissingBuildParameters ? error.versionId : undefined;
- const isOpen = error instanceof MissingBuildParameters;
+ error instanceof ParameterValidationError ? error.versionId : undefined;
+ const isOpen =
+ error instanceof MissingBuildParameters ||
+ error instanceof ParameterValidationError;
return workspace.template_use_classic_parameter_flow ? (
= ({
/>
) : (