FlowiseAI/Flowise · critical · Error

Invalid or unsafe file name: ${name}

Error message

Invalid or unsafe file name: ${name}

What it means

Thrown by sanitizeFileName() after it has stripped the FILE-STORAGE:: prefix, percent-decoded, extracted the basename via path.basename, run it through the sanitize-filename package, and removed leading dots — and the result (`baseName`) is either empty or still flagged by isUnsafeFilePath(). isUnsafeFilePath rejects any remaining `..`, encoded traversal bytes, null/control chars, absolute Unix/Windows roots, or UNC/extended-length prefixes. Reaching this throw means the input survived every normalization step yet is still dangerous, so it is treated as a malicious or corrupt file name.

Source

Thrown at packages/components/src/validator.ts:430

    // Strip the FILE-STORAGE:: prefix if present
    let stripped = name.replace(/^FILE-STORAGE::/, '')
    // Decode percent-encoded traversal sequences before basename extraction
    try {
        stripped = decodeURIComponent(stripped)
    } catch (_) {
        // If decoding fails the raw string is fine — basename will still strip dirs
    }
    // Normalize backslashes to forward slashes so path.basename works on all
    // platforms (on Linux, path.basename does not treat \ as a separator)
    stripped = stripped.replace(/\\/g, '/')
    // Extract only the base filename — removes all directory components
    let baseName = path.basename(stripped)
    // Run through sanitize-filename to strip OS-reserved chars, control chars, etc.
    baseName = sanitize(baseName)
    // Remove leading dots to prevent hidden files or relative path references
    baseName = baseName.replace(/^\.+/, '')
    if (!baseName || isUnsafeFilePath(baseName)) {
        throw new Error(`Invalid or unsafe file name: ${name}`)
    }
    return baseName
}

/**
 * Safely resolve an untrusted relative key/filename to an absolute path inside a
 * trusted base directory, guaranteeing the result cannot escape that directory.
 *
 * @param {string} baseDir The trusted base directory (e.g. a freshly created temp dir)
 * @param {string} key The untrusted relative key or filename
 * @returns {string} A validated absolute path guaranteed to be within baseDir
 * @throws {Error} If key is missing/invalid or the resolved path escapes baseDir
 */
export const getSafeFilePath = (baseDir: string, key: string): string => {
    if (!key || typeof key !== 'string') {
        throw new Error('Invalid file path: key is required and must be a string')
    }

View on GitHub (pinned to abe4a8601a)

Solutions

  1. Log the offending `name` server-side to identify whether it is malicious traffic or a legitimate edge case.
  2. If legitimate, generate a safe replacement name (UUID) instead of forwarding the raw value, then retry the operation.
  3. Sanitize/normalize the input at the source (client or upstream service) so it contains only alphanumerics, dash, underscore, dot before reaching Flowise.
  4. Verify PATH_TRAVERSAL_SAFETY is intentionally left at its safe default ('false' disables the isUnsafeFilePath check entirely and must never be set in production).

Example fix

// before
const safe = sanitizeFileName(userSuppliedName) // throws on residual unsafe content

// after
let safe: string
try {
    safe = sanitizeFileName(userSuppliedName)
} catch {
    safe = crypto.randomUUID() // fall back to a guaranteed-safe name
}
logger.warn('Rejected unsafe filename; substituted generated name', { original: userSuppliedName })
Defensive patterns

Strategy: validation

Validate before calling

// Pre-screen with the same predicate the sanitizer uses
if (isUnsafeFilePath(name)) {
    return res.status(400).json({ message: 'Unsafe file name' })
}
let safe: string
try {
    safe = sanitizeFileName(name)
} catch {
    safe = crypto.randomUUID() // or reject
}

Try / catch

try {
    const safe = sanitizeFileName(name)
} catch (e) {
    logger.warn('sanitizeFileName rejected input', { name, err: (e as Error).message })
    // do NOT strip chars and retry with the same name — treat as malicious/corrupt
    return res.status(400).json({ message: 'Invalid file name' })
}

Prevention

When it happens

Trigger: A name composed entirely of reserved/control characters that sanitize-filename strips to an empty string (e.g. a name of all dots, all backslashes, or all control bytes); a name whose decoded form still contains `..` after basename extraction on a platform where separators behave unexpectedly; a deliberately crafted payload like `....//....//etc/passwd` that reduces to a traversal fragment; a name with embedded null or control characters that survive sanitization.

Common situations: Penetration testing / fuzzing of the upload API; a storage migration that reintroduces raw user-supplied names; PATH_TRAVERSAL_SAFETY left enabled (the default) while a client sends OS-reserved or encoded payloads; a filename that legitimately had only an extension with no base portion and got stripped to nothing.

Related errors


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