{"record":{"id":"77ad1fa32e155ffa","repo":"HeyPuter/puter","slug":"authentication-required-77ad1f","errorCode":null,"errorMessage":"Authentication required","messagePattern":"Authentication required","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"extensions/installedApps.ts","lineNumber":29,"sourceCode":"    'name',\n    'uid',\n    'title',\n    'installed_at',\n] as const;\nconst ORDER_BY_FIELD_MAP: Record<string, string> = {\n    id: 'apps.id',\n    name: 'apps.name',\n    uid: 'apps.uid',\n    title: 'apps.title',\n    installed_at: 'installed_at',\n};\n\nexport const handleInstalledApps = async (\n    req: Request,\n    res: Response,\n): Promise<void> => {\n    const actor = Context.get('actor');\n    if (!actor?.user?.id) throw new HttpError(401, 'Authentication required');\n\n    const orderBy = String(req.query.orderBy ?? 'installed_at');\n    if (!(ALLOWED_ORDER_BY as readonly string[]).includes(orderBy)) {\n        throw new HttpError(\n            400,\n            `Invalid orderBy. Allowed: ${ALLOWED_ORDER_BY.join(', ')}`,\n        );\n    }\n\n    const page = Math.max(Number(req.query.page) || 1, 1);\n    const limit = Math.min(Math.max(Number(req.query.limit) || 100, 1), 100);\n    const offset = (page - 1) * limit;\n    const orderByField = ORDER_BY_FIELD_MAP[orderBy];\n    const sortDirection = req.query.desc ? 'DESC' : 'ASC';\n\n    const installedApps = (await clients.db.read(\n        `SELECT\n            apps.name,","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/extensions/installedApps.ts#L11-L47","documentation":"TogetherVideoProvider.generate validates that `prompt` is a non-empty string before resolving the model or honoring test_mode. Same guard and same legacyCode as the other providers; the first thing generate() does after destructuring params.","triggerScenarios":"Calling the Together generate path with prompt omitted, null, undefined, non-string, or empty/whitespace. Same shape as the Gemini/OpenAI prompt guard.","commonSituations":"Form submitted with empty prompt; prompt read from a missing config key; positional arg passed where a params object was expected.","solutions":["Trim and validate the prompt is non-empty before calling generate().","Gate the submit UI on a non-empty prompt.","Coerce with String(value).trim() at the input boundary.","Confirm prompt is the `prompt` field of the params object."],"exampleFix":"// before\nawait together.generate({ prompt: body.prompt });\n\n// after\nconst prompt = String(body.prompt ?? '').trim();\nif (!prompt) return res.status(400).send('prompt required');\nawait together.generate({ prompt });","handlingStrategy":"validation","validationCode":"function cleanPrompt(p) {\n  if (typeof p !== 'string') throw new Error('prompt must be a non-empty string');\n  const trimmed = p.trim();\n  if (!trimmed) throw new Error('prompt must be a non-empty string');\n  return trimmed;\n}","typeGuard":"function isNonEmptyPrompt(p) { return typeof p === 'string' && p.trim().length > 0; }","tryCatchPattern":null,"preventionTips":["Validate the prompt at the form boundary before calling Together.","Do not default the prompt to a placeholder string.","Pass prompt as the `prompt` key of the params object."],"tags":["ai-video","together","validation","prompt"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}