{"record":{"id":"624762f4d34dc2ae","repo":"HeyPuter/puter","slug":"bad-request-624762","errorCode":"bad_request","errorMessage":"Missing `key`","messagePattern":"Missing `key`","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"warning","filePath":"src/backend/drivers/kv/KVStoreDriver.ts","lineNumber":140,"sourceCode":"                    [DEFAULT_FREE_SUBSCRIPTION]: 3,\n                    [DEFAULT_TEMP_SUBSCRIPTION]: 2,\n                },\n            },\n        },\n    };\n\n    override getReportedCosts(): Record<string, unknown>[] {\n        return Object.entries(KV_COSTS).map(([usageType, ucentsPerUnit]) => ({\n            usageType,\n            ucentsPerUnit,\n            unit: 'capacity-unit',\n            source: 'driver:kvStore',\n        }));\n    }\n\n    #coerceKey(key: unknown): string {\n        if (key === null || key === undefined) {\n            throw new HttpError(400, 'Missing `key`', {\n                legacyCode: 'bad_request',\n            }); // legacyCode for backward compatibility with old error handling in controllers\n        }\n        const str = typeof key === 'string' ? key : String(key);\n        if (str === '')\n            throw new HttpError(400, 'Missing `key`', {\n                legacyCode: 'bad_request',\n            }); // legacyCode for backward compatibility with old error handling in controllers\n        return str;\n    }\n\n    async #opts(method: string, args: KvCallArgs): Promise<KVOpts> {\n        const actor = Context.get('actor') as Actor | undefined;\n\n        // Every method resolves its options here first, so this is the one\n        // place the budget gate has to go. `CREDIT_UNGATED_KV_METHODS` is what\n        // stays reachable after it: an account that has run out still has to be\n        // able to get its data out of the way of the next thing it stores.","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/kv/KVStoreDriver.ts#L122-L158","documentation":"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.","triggerScenarios":"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)`.","commonSituations":"A variable that was expected to hold a key but was never assigned; destructuring that produced undefined; a caller forgetting the key positional argument.","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."],"exampleFix":"// before\nkv.set(maybeUndefined, value);\n\n// after\nif (typeof key !== 'string' || !key) throw new Error('key required');\nkv.set(key, value);","handlingStrategy":"validation","validationCode":"function requireKey(key) {\n  if (key === null || key === undefined) throw new Error('Missing key');\n  return String(key);\n}\nkv.set(requireKey(key), value);","typeGuard":"/** @param {unknown} key @returns {boolean} */\nfunction isPresentKey(key) { return key !== null && key !== undefined; }","tryCatchPattern":"try {\n  await kv.set(key, value);\n} catch (e) {\n  if (e.code === 'bad_request' && e.message === 'Missing `key`') { /* fix the caller */ return; }\n  throw e;\n}","preventionTips":["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."],"tags":["kv","validation","bad-request","driver"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}