HeyPuter/puter · error · HttpError

Invalid orderBy. Allowed: ${ALLOWED_ORDER_BY.join(', ')}

Error message

Invalid orderBy. Allowed: ${ALLOWED_ORDER_BY.join(', ')}

What it means

TogetherVideoProvider reads `Context.get('actor')` before the credit check. The actor is required both to call hasEnoughCredits and later to incrementUsage on the actor. If the actor is absent, the request reached the provider without authentication context and the driver throws 401 unauthorized rather than producing unbilled output.

Source

Thrown at extensions/installedApps.ts:33

] as const;
const ORDER_BY_FIELD_MAP: Record<string, string> = {
    id: 'apps.id',
    name: 'apps.name',
    uid: 'apps.uid',
    title: 'apps.title',
    installed_at: 'installed_at',
};

export const handleInstalledApps = async (
    req: Request,
    res: Response,
): Promise<void> => {
    const actor = Context.get('actor');
    if (!actor?.user?.id) throw new HttpError(401, 'Authentication required');

    const orderBy = String(req.query.orderBy ?? 'installed_at');
    if (!(ALLOWED_ORDER_BY as readonly string[]).includes(orderBy)) {
        throw new HttpError(
            400,
            `Invalid orderBy. Allowed: ${ALLOWED_ORDER_BY.join(', ')}`,
        );
    }

    const page = Math.max(Number(req.query.page) || 1, 1);
    const limit = Math.min(Math.max(Number(req.query.limit) || 100, 1), 100);
    const offset = (page - 1) * limit;
    const orderByField = ORDER_BY_FIELD_MAP[orderBy];
    const sortDirection = req.query.desc ? 'DESC' : 'ASC';

    const installedApps = (await clients.db.read(
        `SELECT
            apps.name,
            apps.uid,
            apps.title,
            apps.description,
            apps.icon,

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Route generation through an authenticated controller/driver method.
  2. Assert Context.actor is set before invoking the provider; fail fast with 401 if not.
  3. Add a regression test asserting unauthenticated calls return 401.
  4. Mirror the auth/subdomain RouteOptions used by the other video providers.

Example fix

// before — driver route without auth
extension.post('/drivers/ai-video/together', (req, res) => together.generate(req.body));

// after — auth gate populates Context.actor
extension.post('/drivers/ai-video/together', { auth: true }, (req, res) => together.generate(req.body));
Defensive patterns

Strategy: try-catch

Validate before calling

// client side
if (!puter.auth.isSignedIn()) { redirectToLogin(); return; }
await puter.ai.txt2video({ prompt, model: 'together/...' });

Try / catch

try { await puter.ai.txt2video(params); }
catch (e) {
  if (e?.code === 'unauthorized') { await puter.auth.signIn(); return; }
  throw e;
}

Prevention

When it happens

Trigger: The Together generate route is hit without the auth gate, or the provider is invoked from a path (background job, test, miswired extension) that never populated Context.actor.

Common situations: Extension route missing `auth: true`; refactor that relocated the generate call outside the authenticated controller; test harness that instantiates the provider directly.

Related errors


AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12). Data as JSON: /api/errors/c5e146c8d0f80bf4. Report an issue: GitHub.