windmill-labs/windmill · error

Invalid key

Error message

Invalid key

What it means

The same eval sandbox Proxy for the global state validates that every assigned key is a string before writing it and syncing the value into the World store. Symbol or non-string keys (e.g. from programmatic/spread logic, Symbol.iterator, or obfuscated expression code) throw 'Invalid key'.

Source

Thrown at frontend/src/lib/components/apps/components/helpers/eval.ts:151

	runnableComponents: Record<string, { cb?: (() => void)[] }>,
	noReturn: boolean,
	groupContextId: string | undefined,
	globalRecomputeFunction: ((excludeIds?: string) => void) | undefined
) {
	const createProxy = (name: string, obj: any) => {
		// console.log('Creating proxy', name, obj)
		if (obj != null && obj != undefined && typeof obj == 'object') {
			if (name == 'group' && groupContextId) {
				return createGroupProxy(groupContextId, obj)
			}
			return new Proxy(obj, {
				set(target, key, value) {
					if (name != 'state') {
						throw new Error(
							'Cannot set value on objects that are neither the global state or a container group field'
						)
					}
					if (typeof key !== 'string') {
						throw new Error('Invalid key')
					}
					target[key] = value
					let o = worldStore?.newOutput(name, key, value)
					o?.set(value, true)

					return true
				},
				get(obj, prop) {
					if (name != 'state' && prop == 'group') {
						return createGroupProxy(name, obj[prop])
					} else {
						return obj[prop]
					}
				}
			})
		} else {
			return obj

View on GitHub (pinned to e474e8803c)

Solutions

  1. Use string keys only: ensure the key expression evaluates to a string, e.g. `state[String(key)] = value`
  2. Check for accidental symbol-key assignment (Symbol.iterator, Symbol.asyncIterator) — spread those objects explicitly instead
  3. If copying objects, assign the whole object once (`state.items = obj`) rather than copying its keys individually

Example fix

// before
state[myKey] = value // myKey may be a symbol
// after
if (typeof myKey !== 'string') throw new Error('state keys must be strings')
state[myKey] = value
Defensive patterns

Strategy: validation

Validate before calling

if (typeof key !== 'string') {
  throw new Error(`state keys must be strings, got ${typeof key}`)
}
state[key] = value

Type guard

function isStringKey(key: PropertyKey): key is string {
  return typeof key === 'string'
}

Try / catch

try {
  state[key] = value
} catch (e) {
  if (e.message === 'Invalid key') {
    state[String(key)] = value
  } else throw e
}

Prevention

When it happens

Trigger: Assigning a computed property with a Symbol key to state inside an app expression, e.g. `state[symbolKey] = v`, or library/user code that runs Object.assign-like logic with symbol keys against the state proxy, or numeric keys coerced from exotic paths.

Common situations: Using spread or utility functions that copy symbols onto state; defining custom Symbol.toPrimitive/iterator interactions on objects assigned into state; writing expression code with dynamic bracket access where the key evaluates to a symbol.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/bf6f2d7b6f74ddb4. Report an issue: GitHub.