windmill-labs/windmill · error · Error
An empty string is not a valid value for secret variable "${
Error message
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. What it means
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.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:4676
function resolveVariableWrite(
args: WriteVariableArgs,
base?: VariableDraftState
): {
is_secret: boolean
value: string
description: string
} {
if (base === undefined && (args.value === undefined || args.is_secret === undefined)) {
throw new Error(
`Variable "${args.path}" does not exist yet, so creating it requires both value and is_secret.`
)
}
const is_secret = args.is_secret ?? base?.variable.is_secret ?? false
// '' is the sentinel for "nothing staged" in a secret draft, so it cannot also mean
// "set the secret to empty". Refusing it matters because a model reaching for a
// placeholder — the habit this schema change removes — would otherwise wipe the secret.
if (is_secret && args.value === '') {
throw new Error(
`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.`
)
}
// Securing one needs a value too when it holds none: the deploy would send no `value`
// (nothing is staged) and the backend refuses an is_secret change without one. Saying
// so here keeps the model from having to interpret that error.
if (
is_secret &&
base?.variable.is_secret === false &&
(args.value ?? base.variable.value) === ''
) {
throw new Error(
`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.`
)
}
// Un-securing always needs a new plaintext value. An `$encrypted:` marker is no
// help: the deploy endpoints only decrypt it while the target stays secret, so
// carrying it into a non-secret variable would store the marker as the value.View on GitHub (pinned to e474e8803c)
Solutions
- Omit the value field entirely to keep the currently stored secret
- Pass the actual new secret value if the secret should change
Example fix
// before
writeVariable({ path: 'u/admin/API_KEY', is_secret: true, value: '' })
// after
writeVariable({ path: 'u/admin/API_KEY', is_secret: true }) // keeps stored secret Defensive patterns
Strategy: validation
Validate before calling
if (isSecret && args.value === '') throw new Error('omit value to keep stored secret'); Type guard
function isValidSecretValue(v) { return v === undefined || (typeof v === 'string' && v.length > 0); } Try / catch
try { writeVariable(args) } catch (e) { if (e.message.includes('empty string is not a valid value')) delete args.value and retry; } Prevention
- Omit value entirely to keep an existing secret
- Never use '' as a placeholder for secrets
When it happens
Trigger: A write_variable call where the resolved is_secret is true and args.value is exactly '' (empty string).
Common situations: The model uses '' as a placeholder value for an unknown secret instead of omitting value to keep the stored one.
Related errors
- Cannot make variable "${args.path}" secret without a value:
- Cannot turn secret variable "${args.path}" into a non-secret
- Draft variable "${draftValue.path}" is secret but stages no
- Variable "${args.path}" does not exist yet, so creating it r
- A variable was created at ${path} while this setup was runni
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/f8757bb60622eb73.
Report an issue: GitHub.