FlowiseAI/Flowise · warning · InternalFlowiseError

Error: apikeyController.createApiKey - permissions must be a

Error message

Error: apikeyController.createApiKey - permissions must be an array of strings!

What it means

createApiKey handler validates that req.body.permissions is a non-empty array where every element is a string. Failing any of those checks throws InternalFlowiseError 412. Permissions are stored on the key and used for authorization, so the shape must be correct before the service layer is reached.

Source

Thrown at packages/server/src/controllers/apikey/index.ts:35

        const apiResponse = await apikeyService.getAllApiKeys(user, page, limit)
        return res.status(StatusCodes.OK).json(apiResponse)
    } catch (error) {
        next(error)
    }
}

const createApiKey = async (req: Request, res: Response, next: NextFunction) => {
    try {
        if (typeof req.body === 'undefined' || !req.body.keyName) {
            throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.createApiKey - keyName not provided!`)
        }
        if (
            !req.body.permissions ||
            !Array.isArray(req.body.permissions) ||
            req.body.permissions.length === 0 ||
            !req.body.permissions.every((p: any) => typeof p === 'string')
        ) {
            throw new InternalFlowiseError(
                StatusCodes.PRECONDITION_FAILED,
                `Error: apikeyController.createApiKey - permissions must be an array of strings!`
            )
        }
        const user = req.user as LoggedInUser
        const apiResponse = await apikeyService.createApiKey(user, req.body.keyName, req.body.permissions)
        return res.json(apiResponse)
    } catch (error) {
        next(error)
    }
}

// Update api key
const updateApiKey = async (req: Request, res: Response, next: NextFunction) => {
    try {
        if (typeof req.params === 'undefined' || !req.params.id) {
            throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.updateApiKey - id not provided!`)
        }

View on GitHub (pinned to abe4a8601a)

Solutions

  1. Send permissions as a non-empty array of strings, e.g. ["chatflows:read", "chatflows:write"].
  2. On the client, coerce selected permission values to strings and ensure at least one is chosen.
  3. If accepting a string shorthand, wrap it client-side: [].concat(value).
  4. Add a JSON-schema validator at the route to reject malformed arrays with a clearer message.

Example fix

// before
if (!req.body.permissions || !Array.isArray(req.body.permissions) || req.body.permissions.length === 0 || !req.body.permissions.every((p: any) => typeof p === 'string')) {
    throw new InternalFlowiseError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.createApiKey - permissions must be an array of strings!`)
}

// after: clearer status and message
const perms = req.body?.permissions
if (!Array.isArray(perms) || perms.length === 0 || !perms.every((p: unknown): p is string => typeof p === 'string')) {
    throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'permissions must be a non-empty array of strings')
}
Defensive patterns

Strategy: type-guard

Validate before calling

function requirePermissions(body: any): asserts body is { permissions: string[] } {
    const p = body?.permissions
    if (!Array.isArray(p) || p.length === 0 || !p.every((x) => typeof x === 'string')) {
        throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'permissions must be a non-empty array of strings')
    }
}
requirePermissions(req.body)

Type guard

function isStringArray(value: unknown): value is string[] {
    return Array.isArray(value) && value.length > 0 && value.every((x) => typeof x === 'string')
}

Try / catch

// Relies on the global error handler mapping InternalFlowiseError.statusCode (412) to HTTP.
// To improve, change the thrown status to BAD_REQUEST (400) for clearer client semantics.

Prevention

When it happens

Trigger: POST to create-api-key with permissions omitted, set to a single string instead of an array, an empty array, or an array containing non-string entries (numbers, objects, null).

Common situations: Client sends permissions: 'chatflows:read' (string) instead of ['chatflows:read']. Frontend sends [] when nothing selected. Mixed-type array from a loosely typed form. Legacy client using a comma-separated string.

Related errors


AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12). Data as JSON: /api/errors/8ccdbfa043451f8f. Report an issue: GitHub.