{"record":{"id":"f8757bb60622eb73","repo":"windmill-labs/windmill","slug":"an-empty-string-is-not-a-valid-value-for-secret-va","errorCode":null,"errorMessage":"An empty string is not a valid value for secret variable \"${args.path}\". Omit value to keep the stored secret, or pass the real new one.","messagePattern":"An empty string is not a valid value for secret variable \"(.+?)\"\\. Omit value to keep the stored secret, or pass the real new one\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":4676,"sourceCode":"function resolveVariableWrite(\n\targs: WriteVariableArgs,\n\tbase?: VariableDraftState\n): {\n\tis_secret: boolean\n\tvalue: string\n\tdescription: string\n} {\n\tif (base === undefined && (args.value === undefined || args.is_secret === undefined)) {\n\t\tthrow new Error(\n\t\t\t`Variable \"${args.path}\" does not exist yet, so creating it requires both value and is_secret.`\n\t\t)\n\t}\n\tconst is_secret = args.is_secret ?? base?.variable.is_secret ?? false\n\t// '' is the sentinel for \"nothing staged\" in a secret draft, so it cannot also mean\n\t// \"set the secret to empty\". Refusing it matters because a model reaching for a\n\t// placeholder — the habit this schema change removes — would otherwise wipe the secret.\n\tif (is_secret && args.value === '') {\n\t\tthrow new Error(\n\t\t\t`An empty string is not a valid value for secret variable \"${args.path}\". Omit value to keep the stored secret, or pass the real new one.`\n\t\t)\n\t}\n\t// Securing one needs a value too when it holds none: the deploy would send no `value`\n\t// (nothing is staged) and the backend refuses an is_secret change without one. Saying\n\t// so here keeps the model from having to interpret that error.\n\tif (\n\t\tis_secret &&\n\t\tbase?.variable.is_secret === false &&\n\t\t(args.value ?? base.variable.value) === ''\n\t) {\n\t\tthrow new Error(\n\t\t\t`Cannot make variable \"${args.path}\" secret without a value: it currently holds an empty one, so there would be nothing to encrypt. Pass the value it should hold.`\n\t\t)\n\t}\n\t// Un-securing always needs a new plaintext value. An `$encrypted:` marker is no\n\t// help: the deploy endpoints only decrypt it while the target stays secret, so\n\t// carrying it into a non-secret variable would store the marker as the value.","sourceCodeStart":4658,"sourceCodeEnd":4694,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L4658-L4694","documentation":"An empty string in the secret draft means 'nothing staged', so it cannot also mean 'set the secret to empty'. Passing value='' for a secret variable is rejected to prevent a placeholder habit from wiping the stored secret.","triggerScenarios":"A write_variable call where the resolved is_secret is true and args.value is exactly '' (empty string).","commonSituations":"The model uses '' as a placeholder value for an unknown secret instead of omitting value to keep the stored one.","solutions":["Omit the value field entirely to keep the currently stored secret","Pass the actual new secret value if the secret should change"],"exampleFix":"// before\nwriteVariable({ path: 'u/admin/API_KEY', is_secret: true, value: '' })\n// after\nwriteVariable({ path: 'u/admin/API_KEY', is_secret: true }) // keeps stored secret","handlingStrategy":"validation","validationCode":"if (isSecret && args.value === '') throw new Error('omit value to keep stored secret');","typeGuard":"function isValidSecretValue(v) { return v === undefined || (typeof v === 'string' && v.length > 0); }","tryCatchPattern":"try { writeVariable(args) } catch (e) { if (e.message.includes('empty string is not a valid value')) delete args.value and retry; }","preventionTips":["Omit value entirely to keep an existing secret","Never use '' as a placeholder for secrets"],"tags":["ai-copilot","secrets","variables"],"backgroundTag":"empty-secret-value","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"}