{"record":{"id":"a541a1fcbc4215ae","repo":"HeyPuter/puter","slug":"appid-parameter-is-required","errorCode":null,"errorMessage":"appId parameter is required","messagePattern":"appId parameter is required","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"extensions/metering.ts","lineNumber":75,"sourceCode":"    const actor = Context.get('actor');\n    if (!actor?.user) throw new HttpError(401, 'Authentication required');\n\n    const [actorUsage, allowanceInfo] = await Promise.all([\n        services.metering.getActorCurrentMonthUsageDetails(actor),\n        services.metering.getAllowedUsage(actor),\n    ]);\n    res.json({ ...actorUsage, allowanceInfo });\n};\n\nexport const handleMeteringUsageForApp = async (\n    req: Request,\n    res: Response,\n): Promise<void> => {\n    const actor = Context.get('actor');\n    if (!actor?.user) throw new HttpError(401, 'Authentication required');\n\n    let appId = String(req.params.appIdOrName ?? '');\n    if (!appId) throw new HttpError(400, 'appId parameter is required');\n\n    // If not a UUID-shaped app UID, look up by name\n    if (!appId.startsWith('app-')) {\n        const appRows = (await clients.db.read(\n            'SELECT `uid` FROM `apps` WHERE `name` = ? LIMIT 1',\n            [appId],\n        )) as Array<{ uid: string }>;\n        if (appRows.length > 0) {\n            appId = appRows[0].uid;\n        } else {\n            throw new HttpError(404, 'App not found');\n        }\n    }\n\n    const appUsage =\n        await services.metering.getActorCurrentMonthAppUsageDetails(\n            actor,\n            appId,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/extensions/metering.ts#L57-L93","documentation":"AppDriver.create requires an `object` argument carrying the new app's fields. The very first check rejects when object is falsy or not an object type. This runs before actor/permission validation, so a malformed request body fails fast with 400 bad_request before any auth or dedupe logic.","triggerScenarios":"Calling puter.apps.create() with no arguments, with only options and no object, or with a non-object payload (string, array, number) as the first arg. Typically a serialization bug or a wrong call signature in the client.","commonSituations":"Client serialized the form as a query string instead of JSON; positional-arg confusion (passing the name string where the object belongs); SDK misuse where create() was called with only an options bag.","solutions":["Pass a plain object: puter.apps.create({ name, index_url, ... }).","On the server, JSON-parse and type-check req.body before invoking create().","Confirm Content-Type: application/json is set on the request.","Cross-check the call signature against the puter.js Apps module."],"exampleFix":"// before\nputer.apps.create();\nputer.apps.create('my-app');\nputer.apps.create({ name: 'my-app' }, null);\n\n// after\nputer.apps.create({ name: 'my-app', index_url: 'https://my.app/' });","handlingStrategy":"type-guard","validationCode":"function isPlainObject(v) { return v != null && typeof v === 'object' && !Array.isArray(v); }\nif (!isPlainObject(appFields)) throw new Error('create() requires an object');\nawait puter.apps.create(appFields);","typeGuard":"function isAppCreateObject(v) {\n  return v != null && typeof v === 'object' && !Array.isArray(v) && typeof v.name === 'string';\n}","tryCatchPattern":null,"preventionTips":["Send Content-Type: application/json and a JSON object body.","Pass the fields object as the first arg, options as the second.","Unit-test the client serializer to confirm it emits an object, not a query string."],"tags":["apps","validation","api-shape"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}