danny-avila/LibreChat · error · Error

Storage backend "${source}" does not support file writes

Error message

Storage backend "${source}" does not support file writes

What it means

Thrown by resolveSkillStorage in the skills route when the file strategy resolved for the skill_file context has no saveBuffer function. The selected storage backend (e.g. local, s3) must implement buffer writes for skill file creation/import to work; a backend that only supports reads is rejected.

Source

Thrown at api/server/routes/skills.js:150

      ? withDeploymentSkillIds(await findPubliclyAccessibleResources(params))
      : findPubliclyAccessibleResources(params),
  hasPublicPermission: async (params) =>
    params.resourceType === 'skill' && params.requiredPermissions === PermissionBits.VIEW
      ? withDeploymentSkillIds([]).some((id) => id.toString() === params.resourceId.toString()) ||
        hasPublicPermission(params)
      : hasPublicPermission(params),
  grantPermission,
  isValidObjectIdString,
});

// ---------------------------------------------------------------------------
// File storage helper: resolve the active strategy's saveBuffer
// ---------------------------------------------------------------------------
function resolveSkillStorage(req, { isImage = false } = {}) {
  const source = getFileStrategy(req.config, { context: FileContext.skill_file, isImage });
  const strategy = getStrategyFunctions(source);
  if (!strategy.saveBuffer) {
    throw new Error(`Storage backend "${source}" does not support file writes`);
  }
  return { saveBuffer: strategy.saveBuffer, source };
}

// ---------------------------------------------------------------------------
// Import handler (zip/md/skill → create skill + files)
// ---------------------------------------------------------------------------
const importHandler = createImportHandler({
  limits: (req) => ({
    maxZipBytes: getSkillImportSizeLimit(req),
  }),
  createSkill,
  getSkillById,
  deleteSkill,
  upsertSkillFile,
  saveBuffer: (req, { userId, buffer, fileName, basePath, isImage, tenantId }) => {
    const requestTenantId = tenantId ?? resolveRequestTenantId(req);
    const storage = resolveSkillStorage(req, { isImage });

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Point the skill_file context at a strategy that implements saveBuffer (local or s3).
  2. If using a custom strategy, implement and register a saveBuffer method on it.
  3. Verify FILE_STRATEGY / getFileStrategy config returns the expected backend for the skill context.
  4. Disable skill file import/upload until a writable backend is configured.
Defensive patterns

Strategy: validation

Validate before calling

const strategy = getStrategyFunctions(source);
if (typeof strategy.saveBuffer !== 'function') {
  throw new Error(`Refusing skill import: backend '${source}' cannot write files`);
}

Type guard

function strategyCanWrite(strategy) {
  return strategy != null && typeof strategy.saveBuffer === 'function';
}

Try / catch

try { await importHandler(req, res); }
catch (e) { if (/does not support file writes/.test(e.message)) return res.status(400).json({ error: 'Configured storage cannot receive skill files' }); throw e; }

Prevention

When it happens

Trigger: The configured FILE_STRATEGY (or the value returned by getFileStrategy for the skill_file context) points at a read-only or partially-implemented strategy whose getStrategyFunctions result lacks saveBuffer.

Common situations: A custom storage plugin registered without a saveBuffer implementation; deployment wired to a read-only CDN source; a deployment-mode skill file source selected where writes are not supported.

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/ec02fdf35a935f02. Report an issue: GitHub.