From 6eba6fff438b0c3b73fd8daf60c5207aa56da093 Mon Sep 17 00:00:00 2001
From: Roland Tannous
Date: Mon, 9 Mar 2026 16:20:00 +0000
Subject: [PATCH] fix: disable Start Training when eval_steps set without eval
split
---
.../studio/sections/training-section.tsx | 9 ++++++--
.../frontend/src/features/training/index.ts | 1 +
.../src/features/training/lib/validation.ts | 22 +++++++++++--------
3 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/studio/frontend/src/features/studio/sections/training-section.tsx b/studio/frontend/src/features/studio/sections/training-section.tsx
index 6f0297b03f..43cc2f889e 100644
--- a/studio/frontend/src/features/studio/sections/training-section.tsx
+++ b/studio/frontend/src/features/studio/sections/training-section.tsx
@@ -12,6 +12,7 @@ import {
serializeConfigToYaml,
useTrainingActions,
useTrainingConfigStore,
+ validateTrainingConfig,
} from "@/features/training";
import {
Archive04Icon,
@@ -43,7 +44,8 @@ export function TrainingSection() {
const { isStarting, startError, startTrainingRun } = useTrainingActions();
const isIncompatible =
!store.isVisionModel && store.isDatasetImage === true;
- const fileInputRef = useRef(null);
+ const configValidation = validateTrainingConfig(store);
+ const fileInputRef = useRef(null);
const handleFileUpload = (e: React.ChangeEvent) => {
const file = e.target.files?.[0];
@@ -150,7 +152,7 @@ export function TrainingSection() {
data-tour="studio-start"
className="w-full cursor-pointer bg-gradient-to-r from-emerald-500 to-teal-500 text-white hover:from-emerald-600 hover:to-teal-600"
onClick={() => void startTrainingRun()}
- disabled={isStarting || isIncompatible}
+ disabled={isStarting || isIncompatible || !configValidation.ok}
>
{isStarting ? "Starting..." : "Start Training"}
@@ -163,6 +165,9 @@ export function TrainingSection() {
Text model is not compatible with a multimodal dataset. Switch to a vision model or choose a text-only dataset.
)}
+ {!configValidation.ok && configValidation.message && !isIncompatible && (
+ {configValidation.message}
+ )}
{/* Upload / Save / Reset */}
Training Config
diff --git a/studio/frontend/src/features/training/index.ts b/studio/frontend/src/features/training/index.ts
index d2fd4f2e43..9da25f88bb 100644
--- a/studio/frontend/src/features/training/index.ts
+++ b/studio/frontend/src/features/training/index.ts
@@ -11,3 +11,4 @@ export { listLocalModels } from "./api/models-api";
export type { LocalModelInfo } from "./api/models-api";
export type { TrainingPhase } from "./types/runtime";
export { parseYamlConfig, serializeConfigToYaml } from "./lib/yaml-config";
+export { validateTrainingConfig } from "./lib/validation";
diff --git a/studio/frontend/src/features/training/lib/validation.ts b/studio/frontend/src/features/training/lib/validation.ts
index ae89cdaf51..6ca95c4b02 100644
--- a/studio/frontend/src/features/training/lib/validation.ts
+++ b/studio/frontend/src/features/training/lib/validation.ts
@@ -16,18 +16,22 @@ export function validateTrainingConfig(
if (!config.dataset) {
return { ok: false, message: "Select a Hugging Face dataset first." };
}
- return { ok: true, message: null };
- }
-
- if (config.datasetSource === "upload") {
+ } else if (config.datasetSource === "upload") {
if (!config.uploadedFile) {
return { ok: false, message: "Select a local dataset first." };
}
- return { ok: true, message: null };
+ } else {
+ return { ok: false, message: "Unsupported dataset source." };
}
- return {
- ok: false,
- message: "Unsupported dataset source.",
- };
+ // Eval steps requires an eval split to be selected
+ if (config.evalSteps > 0 && !config.datasetEvalSplit) {
+ return {
+ ok: false,
+ message:
+ "Eval Steps is set but no Eval Split is selected. Choose an Eval Split or set Eval Steps to 0.",
+ };
+ }
+
+ return { ok: true, message: null };
}