{"record":{"id":"64f9027c18018f4b","repo":"windmill-labs/windmill","slug":"cannot-make-variable-args-path-secret-without","errorCode":null,"errorMessage":"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.","messagePattern":"Cannot make variable \"(.+?)\" secret without a value: it currently holds an empty one, so there would be nothing to encrypt\\. Pass the value it should hold\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/global/core.ts","lineNumber":4688,"sourceCode":"\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.\n\tif (is_secret === false && base?.variable.is_secret === true && args.value === undefined) {\n\t\tthrow new Error(\n\t\t\t`Cannot turn secret variable \"${args.path}\" into a non-secret one without a value: its stored value cannot be read, so it would be replaced by an empty one. Pass the new plaintext value, or leave is_secret unset to keep it secret.`\n\t\t)\n\t}\n\treturn {\n\t\tis_secret,\n\t\tvalue: args.value ?? base?.variable.value ?? '',\n\t\tdescription: args.description ?? base?.variable.description ?? ''\n\t}\n}\n","sourceCodeStart":4670,"sourceCodeEnd":4706,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/global/core.ts#L4670-L4706","documentation":"Converting a non-secret variable to secret requires a value to encrypt. If the variable currently holds an empty string and no new value is supplied, there is nothing to encrypt, so the deploy would be invalid; this error surfaces that constraint up front.","triggerScenarios":"A write_variable call with is_secret=true (explicitly or resolved) where the base variable is_secret is false and (args.value ?? base.variable.value) === '' — i.e. switching an empty non-secret variable to secret without providing a value.","commonSituations":"The model hardens an empty placeholder variable into a secret without staging a value first.","solutions":["Pass the value the variable should hold along with is_secret: true","If the variable should stay empty, keep it non-secret"],"exampleFix":"// before\nwriteVariable({ path: 'u/admin/TOKEN', is_secret: true }) // currently holds ''\n// after\nwriteVariable({ path: 'u/admin/TOKEN', is_secret: true, value: 'real-token' })","handlingStrategy":"validation","validationCode":"if (args.is_secret && !existingIsSecret && (args.value ?? current) === '') {\n  throw new Error('provide a value to encrypt');\n}","typeGuard":"function canMakeSecret(args, base) { return args.is_secret !== true || typeof args.value === 'string' && args.value !== ''; }","tryCatchPattern":"try { writeVariable(args) } catch (e) { if (e.message.includes('nothing to encrypt')) addValue(args); }","preventionTips":["Always stage a real value when securing a variable","Keep empty variables non-secret"],"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"}