{"record":{"id":"144118a22ead34c6","repo":"windmill-labs/windmill","slug":"validationerrors-join-n","errorCode":null,"errorMessage":"validationErrors.join('\\n')","messagePattern":"validationErrors\\.join\\('\\\\n'\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/FlowBuilder.svelte","lineNumber":467,"sourceCode":"\t\t\tif (flow.value?.modules) {\n\t\t\t\tconst validationErrors: string[] = []\n\t\t\t\tdfsApply(flow.value.modules, (module) => {\n\t\t\t\t\tconst error = validateRetryConfig(module.retry)\n\t\t\t\t\tif (error) {\n\t\t\t\t\t\tvalidationErrors.push(`Step '${module.id}': ${error}`)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tif (flow.value.failure_module) {\n\t\t\t\t\t// add validation logic here for failure module\n\t\t\t\t}\n\n\t\t\t\tif (flow.value.preprocessor_module) {\n\t\t\t\t\t// add validation logic here for preprocessor module\n\t\t\t\t}\n\n\t\t\t\tif (validationErrors.length > 0) {\n\t\t\t\t\tthrow new Error(validationErrors.join('\\n'))\n\t\t\t\t}\n\t\t\t}\n\t\t\t// console.log('flow', computeUnlockedSteps(flow)) // del\n\t\t\t// loadingSave = false // del\n\t\t\t// return\n\n\t\t\t// `newFlow` comes from the embedder, and updating a path that has no\n\t\t\t// deployed flow 404s. Confirm with the server before taking the update\n\t\t\t// branch so a first deploy still lands.\n\t\t\tlet isNewFlow = newFlow\n\t\t\tif (!isNewFlow) {\n\t\t\t\ttry {\n\t\t\t\t\tisNewFlow =\n\t\t\t\t\t\tinitialPath === '' ||\n\t\t\t\t\t\t!(await FlowService.existsFlowByPath({ workspace: opWorkspace!, path: initialPath }))\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Unreachable check: keep the caller's intent rather than failing the deploy.\n\t\t\t\t\tconsole.error('Could not check flow existence', err)","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/FlowBuilder.svelte#L449-L485","documentation":"In frontend/src/lib/components/FlowBuilder.svelte:467, saveFlow() collects client-side validationErrors and throws them joined by newlines, aborting the save. The message is the aggregated list of flow validation problems (missing required fields, invalid module settings, etc.).","triggerScenarios":"Clicking save/deploy while the flow has validation problems the builder detected — e.g. required step inputs empty, invalid paths, malformed schedule/suspend settings.","commonSituations":"Deleting a module a sibling still references; leaving a required input or summary empty; editing JSON forms manually and breaking constraints.","solutions":["Read the multi-line message — each line is one problem — and fix them in the builder UI","Check required fields on each step (inputs, summary)","Remove/reconnect dangling references to deleted steps","Re-open the flow if the editor state went stale, then re-validate"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const errs = validateFlowLocally(flow.value);\nif (errs.length > 0) {\n  sendUserToast('Fix validation errors before saving:\\n' + errs.join('\\n'), true);\n  return; // don't call saveFlow\n}","typeGuard":"null","tryCatchPattern":"try {\n  await saveFlow();\n} catch (e) {\n  if (typeof e.message === 'string' && e.message.includes('\\n')) {\n    e.message.split('\\n').forEach(showFieldError);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Fill required fields (inputs, summary) on every step before saving","Validate locally and show inline errors instead of waiting for save to fail","Re-check references after deleting or reordering modules"],"tags":["flow","validation","ui"],"backgroundTag":"flow-validation-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}