HeyPuter/puter · error · HttpError
forbidden
forbidden
Error message
kv: `${method}` is not available on another app's data What it means
Thrown by `#assertCrossAppKvAccess` when a KV method is invoked against another app's namespace but the method has no mapped cross-app operation in `APP_DATA_KV_METHOD_OPS` (value is `null` or `undefined`). `flush` maps to `null` (namespace-wide, not an entry op), so it can never run cross-app; any unmapped method also fails closed. Returns HTTP 403 with legacyCode `forbidden`.
Source
Thrown at src/backend/drivers/kv/KVStoreDriver.ts:205
{ legacyCode: 'bad_request' },
);
}
await this.#assertCrossAppKvAccess(actor!, appUuid, method, args);
return { actor, namespaceAppUuid: appUuid };
}
async #assertCrossAppKvAccess(
actor: Actor,
targetAppUid: string,
method: string,
args: KvCallArgs,
): Promise<void> {
// `null` = no scope reaches it (`flush` is namespace-wide, not an
// entry op); `undefined` = unmapped method. Both fail closed.
const op = APP_DATA_KV_METHOD_OPS[method];
if (!op) {
throw new HttpError(
403,
`kv: \`${method}\` is not available on another app's data`,
{ legacyCode: 'forbidden' },
);
}
const target = await this.stores.app.getByUid(targetAppUid);
if (!target) {
throw new HttpError(404, `entity_not_found: app:${targetAppUid}`, {
legacyCode: 'subject_does_not_exist',
});
}
if (!appDataSharingAllowed(target)) {
throw new HttpError(
403,
'kv: this app does not share its data with other apps',
{ legacyCode: 'forbidden' },
);View on GitHub (pinned to 908ec23eda)
Solutions
- Do not flush another app's namespace; flush is own-namespace only.
- For cross-app data, restrict to mapped entry ops (get/set/list/del/incr/decr/update/expire/expireAt/add/batchPut).
- Remove the `appUuid` override to operate on your own namespace.
Example fix
// before
kv.flush({ appUuid: otherAppUid }); // 403
// after
// flush only your own namespace
kv.flush();
// or target specific keys cross-app
await kv.del('k', { appUuid: otherAppUid }); Defensive patterns
Strategy: validation
Validate before calling
const CROSS_APP_OK = new Set(['get','set','list','del','remove','incr','decr','add','update','batchPut','expire','expireAt']);
if (appUuid && appUuid !== ownAppUid && !CROSS_APP_OK.has(method)) {
throw new Error(`${method} is not available on another app's data`);
} Type guard
/** @param {string} method @param {string|undefined} appUuid @param {string} ownUid @returns {boolean} */
function isAllowedCrossAppMethod(method, appUuid, ownUid) {
if (!appUuid || appUuid === ownUid) return true;
return new Set(['get','set','list','del','remove','incr','decr','add','update','batchPut','expire','expireAt']).has(method);
} Try / catch
try {
await kv.flush({ appUuid });
} catch (e) {
if (e.code === 'forbidden' && e.message.includes('not available on another app')) { /* flush own namespace only */ await kv.flush(); return; }
throw e;
} Prevention
- Never flush another app's namespace; flush is own-namespace only.
- Restrict cross-app KV calls to mapped entry ops.
- Drop the appUuid override when operating on your own data.
When it happens
Trigger: Calling `kv.flush({ appUuid: otherApp })` (the canonical case), or any future/unmapped method, while targeting another app's namespace. The map defines which ops are even eligible for cross-app access.
Common situations: Admin/tooling trying to flush another app's namespace; a generic KV wrapper that forwards arbitrary method names plus an appUuid; assuming flush is permitted because get/set are.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/1746ac7a2ba9adb0.
Report an issue: GitHub.