windmill-labs/windmill · error · Error
Draft variable "${draftValue.path}" is secret but stages no
Error message
Draft variable "${draftValue.path}" is secret but stages no value, so it cannot be created. Run write_variable with its value first. What it means
When deploying a new variable that does not exist yet, a secret variable must carry a staged value. If the draft's value is '' (the 'nothing staged' sentinel), the backend would create an empty secret, so this error tells the model to stage the value first.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:4732
path: args.path,
variable: {
value,
is_secret,
description
},
labels: args.labels ?? base?.labels,
wsSpecific: args.ws_specific ?? base?.wsSpecific ?? false,
account: args.account ?? base?.account,
is_oauth: args.is_oauth ?? base?.is_oauth,
expires_at: args.expires_at ?? base?.expires_at
}
}
// The deploy body for a variable that does not exist yet.
function buildVariableCreateRequestBody(draftValue: CreateVariable): CreateVariable {
const requestBody = structuredClone(draftValue)
if (requestBody.is_secret && requestBody.value === '') {
throw new Error(
`Draft variable "${draftValue.path}" is secret but stages no value, so it cannot be created. Run write_variable with its value first.`
)
}
return requestBody
}
// The deploy body for an existing variable. Every field is optional on the update
// endpoint, and a draft stores '' when it stages no new value — so omitting `value` in
// that case is what leaves the stored one untouched. A staged value is sent as-is: the
// endpoint decrypts an `$encrypted:` marker and encrypts plaintext.
function buildVariableUpdateRequestBody(
draftValue: CreateVariable
): Omit<CreateVariable, 'value'> & { value?: string } {
const { value, ...rest } = structuredClone(draftValue)
// A value the draft is never allowed to carry — a secret's, and an OAuth-managed one
// dropped by `variableToDraftState` — reaches here as '' whenever this edit staged
// none, so sending it would blank the stored value or wipe a rotating token.
if (rest.is_secret || rest.is_oauth === true || rest.account != undefined) {View on GitHub (pinned to e474e8803c)
Solutions
- Run write_variable with the actual value for the draft path first, then redeploy
- If the variable should not be secret, set is_secret: false so an empty value is allowed
Example fix
// before
deployVariables([{ path: 'u/admin/API_KEY', is_secret: true, value: '' }])
// after
writeVariable({ path: 'u/admin/API_KEY', is_secret: true, value: 'abc123' })
deployVariables([{ path: 'u/admin/API_KEY', is_secret: true, value: 'abc123' }]) Defensive patterns
Strategy: validation
Validate before calling
if (draft.is_secret && draft.value === '') throw new Error('stage the secret value before deploying'); Type guard
function isDeployableSecretDraft(d) { return !d.is_secret || (typeof d.value === 'string' && d.value !== ''); } Try / catch
try { deployVariables(drafts) } catch (e) { if (e.message.includes('stages no value')) await writeVariable({ path, value }) and redeploy; } Prevention
- Stage a value with write_variable before deploying secret drafts
- Never deploy secret drafts with value '' (the nothing-staged sentinel)
When it happens
Trigger: Deploying a draft variable whose requestBody.is_secret is true and value is '' — i.e. buildVariableCreateRequestBody on a create-body with no staged value.
Common situations: The model created a variable draft and forgot to stage a value (or used '' as placeholder) before triggering the deploy.
Related errors
- An empty string is not a valid value for secret variable "${
- Cannot make variable "${args.path}" secret without a value:
- Cannot turn secret variable "${args.path}" into a non-secret
- 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/a0fe3728a683c74f.
Report an issue: GitHub.