HeyPuter/puter · warning · HttpError
bad_request
bad_request
Error message
Missing `key`
What it means
Thrown by `#coerceKey` when the KV `key` argument is `null` or `undefined`. Every KV method runs its key through `#coerceKey`, so a missing key fails fast with HTTP 400 (legacyCode `bad_request`) before any storage/budget work. This mirrors the legacy controller error code for backward compatibility.
Source
Thrown at src/backend/drivers/kv/KVStoreDriver.ts:140
[DEFAULT_FREE_SUBSCRIPTION]: 3,
[DEFAULT_TEMP_SUBSCRIPTION]: 2,
},
},
},
};
override getReportedCosts(): Record<string, unknown>[] {
return Object.entries(KV_COSTS).map(([usageType, ucentsPerUnit]) => ({
usageType,
ucentsPerUnit,
unit: 'capacity-unit',
source: 'driver:kvStore',
}));
}
#coerceKey(key: unknown): string {
if (key === null || key === undefined) {
throw new HttpError(400, 'Missing `key`', {
legacyCode: 'bad_request',
}); // legacyCode for backward compatibility with old error handling in controllers
}
const str = typeof key === 'string' ? key : String(key);
if (str === '')
throw new HttpError(400, 'Missing `key`', {
legacyCode: 'bad_request',
}); // legacyCode for backward compatibility with old error handling in controllers
return str;
}
async #opts(method: string, args: KvCallArgs): Promise<KVOpts> {
const actor = Context.get('actor') as Actor | undefined;
// Every method resolves its options here first, so this is the one
// place the budget gate has to go. `CREDIT_UNGATED_KV_METHODS` is what
// stays reachable after it: an account that has run out still has to be
// able to get its data out of the way of the next thing it stores.View on GitHub (pinned to 908ec23eda)
Solutions
- Always pass a non-empty string key as the first argument.
- Default the key from a checked variable: `kv.set(key ?? '', value)` is wrong — ensure `key` is set upstream.
- Validate inputs at the call boundary before invoking KV.
Example fix
// before
kv.set(maybeUndefined, value);
// after
if (typeof key !== 'string' || !key) throw new Error('key required');
kv.set(key, value); Defensive patterns
Strategy: validation
Validate before calling
function requireKey(key) {
if (key === null || key === undefined) throw new Error('Missing key');
return String(key);
}
kv.set(requireKey(key), value); Type guard
/** @param {unknown} key @returns {boolean} */
function isPresentKey(key) { return key !== null && key !== undefined; } Try / catch
try {
await kv.set(key, value);
} catch (e) {
if (e.code === 'bad_request' && e.message === 'Missing `key`') { /* fix the caller */ return; }
throw e;
} Prevention
- Always pass a defined key as the first positional argument.
- Type your KV call sites so key is `string`, not `string | undefined`.
- Add a unit test that every code path assigns the key before the KV call.
When it happens
Trigger: Calling any KV method (`get`, `set`, `del`, `incr`, etc.) without a key or with `key: null`/`undefined`. E.g. `kv.set(undefined, value)` or `kv.get(null)`.
Common situations: A variable that was expected to hold a key but was never assigned; destructuring that produced undefined; a caller forgetting the key positional argument.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/624762f4d34dc2ae.
Report an issue: GitHub.