April 14, 2026 • 版本: 2026.4.11

硬编码的5MB Media Store限制在Channel限制生效前阻止生成的媒体

OpenClaw在runtime bundle中强制执行硬编码的5MB staging限制,静默覆盖channel和agent-level媒体配置,导致有效的large media工作流出现'Media exceeds 5MB limit'错误。

🔍 症状

硬编码的媒体暂存限制会通过几种不同的错误模式表现出来:

主要错误表现

当尝试暂存或返回超过5MB的媒体文件时,会出现以下错误:

Error: Media exceeds 5MB limit
    at MediaStore.validateSize (dist/store-CA7OW_2w.js:XX:XX)
    at MediaStore.stage (dist/store-CA7OW_2w.js:XX:XX)
    at MediaPipeline.processOutput (dist/store-CA7OW_2w.js:XX:XX)
    at async CodexAgent.handleToolResult (dist/store-CA7OW_2w.js:XX:XX)

媒体处理过程中的错误流程

该错误发生在媒体管道的暂存验证阶段,在任何下游通道特定限制解析之前:

  1. 摄取阶段 — 由代理生成或接收的媒体
  2. 暂存验证dist/store-CA7OW_2w.js 中的硬编码5MB检查 ← 失败点
  3. 通道解析 — 通道/代理媒体限制(从未到达)
  4. 交付 — 输出到配置的通道

触发该问题的条件

  • 生成超过5MB文件的视频生成工作流
  • 帧数据超过阈值的多图像合成或GIF
  • 具有较长持续时间和高采样率的音频文件
  • 任何通过 openclaw.media.stage() 暂存的媒体工具输出

静默覆盖行为

硬编码的限制会静默覆盖有效的通道配置。观察到这种行为的用户可能会注意到:

# Channel config specifies 25MB limit
openclaw config get channels.slack.media.maxBytes
# Output: 26214400 (25MB)

# But generated media still fails at 5MB threshold
openclaw run --tool video-generate --prompt "Generate a 30-second clip"
# Error: Media exceeds 5MB limit

这会造成一种误导性的体验,让人觉得配置的限制无效。

🧠 根因分析

架构问题:过早的验证门

根本原因是 OpenClaw 媒体管道架构中的验证顺序问题。打包的运行时文件 dist/store-CA7OW_2w.js 包含:

// From dist/store-CA7OW_2w.js
const MEDIA_MAX_BYTES = 5 * 1024 * 1024; // 5,242,880 bytes

class MediaStore {
  validateSize(bufferOrPath) {
    const size = this.getSize(bufferOrPath);
    if (size > MEDIA_MAX_BYTES) {
      throw new MediaLimitError(
        `Media exceeds ${MEDIA_MAX_BYTES} limit`
      );
    }
    // ... downstream processing
  }
}

为什么会产生这个问题

  1. 硬编码常量超出配置范围MEDIA_MAX_BYTES 的值在构建时编译到运行时包中,无法通过配置文件、环境变量或运行时设置进行覆盖。
  2. 管道位置 — 验证发生在 MediaStore.validateSize() 方法中,该方法在暂存阶段调用,在任何通道特定限制解析之前执行。
  3. 缺少配置继承 — 暂存层在执行此检查时不查询 channel.media.maxBytesagent.mediaLimits 或任何其他配置层级。
  4. 静默失败路径 — 当超过5MB阈值时,会抛出错误而不引用任何可配置的限值,也不提供关于覆盖暂存上限的指导。

预期与实际的限制层级

OpenClaw 的设计似乎打算从多个来源解析有效限制的限制层级:

// Intended resolution order (never reached):
effectiveLimit = min(
  channel.media.maxBytes,    // e.g., 25MB for Slack
  agent.mediaLimits.maxBytes, // e.g., 10MB for specific agent
  globalDefaults.maxBytes    // e.g., 50MB global ceiling
)

然而,实际的执行路径在任何这些解析路径执行之前就短路到了硬编码的5MB值。

为什么会存在这个问题

5MB 的硬性上限可能最初是作为一种安全措施而出现的,目的是:

  • 防止暂存期间无限制的内存消耗
  • 为"快速"媒体操作强制设定合理的上限
  • 确保暂存层的可预测内存边界

然而,它被实现为一个不透明的常量,而不是具有清晰语义的文档化、可配置参数。

🛠️ 逐步修复

解决方案1:从有效媒体限制派生暂存限制(推荐)

修改 MediaStore 类以从有效配置层级解析暂存限制:

// Before (dist/store-CA7OW_2w.js)
const MEDIA_MAX_BYTES = 5 * 1024 * 1024;

class MediaStore {
  validateSize(bufferOrPath) {
    const size = this.getSize(bufferOrPath);
    if (size > MEDIA_MAX_BYTES) { /* error */ }
  }
}
// After (dist/store-CA7OW_2w.js)
const DEFAULT_STAGING_MAX_BYTES = 50 * 1024 * 1024; // 50MB default

class MediaStore {
  constructor(options = {}) {
    super(options);
    this._effectiveLimit = null;
  }

  getEffectiveLimit(context) {
    if (this._effectiveLimit !== null) {
      return this._effectiveLimit;
    }

    // Resolve from configuration hierarchy
    const configuredLimits = [
      context?.channel?.media?.maxBytes,
      context?.agent?.mediaLimits?.maxBytes,
      context?.config?.media?.stagingMaxBytes
    ].filter(limit => Number.isInteger(limit) && limit > 0);

    this._effectiveLimit = configuredLimits.length > 0
      ? Math.min(...configuredLimits)
      : DEFAULT_STAGING_MAX_BYTES;

    return this._effectiveLimit;
  }

  validateSize(bufferOrPath, context = {}) {
    const size = this.getSize(bufferOrPath);
    const effectiveLimit = this.getEffectiveLimit(context);

    if (size > effectiveLimit) {
      throw new MediaLimitError(
        `Media size (${size} bytes) exceeds effective limit of ${effectiveLimit} bytes ` +
        `(resolved from: ${JSON.stringify(context.channel?.media?.maxBytes ? 'channel' : 'default'})`
      );
    }
  }
}

解决方案2:添加暂存限制配置(临时解决方案)

直到上述修复在版本中可用,将暂存限制添加到配置系统:

步骤1:更新配置模式

# Add to openclaw.config.schema.json or equivalent
{
  "media": {
    "stagingMaxBytes": {
      "type": "integer",
      "default": 52428800,
      "description": "Maximum media size (in bytes) for staging/store operations",
      "env": "OPENCLAW_MEDIA_STAGING_MAX_BYTES"
    }
  }
}

步骤2:修改 MediaStore 初始化

class MediaStore {
  constructor(options = {}) {
    const config = options.config || globalOpenClawConfig;
    this._stagingLimit = config.media?.stagingMaxBytes || DEFAULT_STAGING_MAX_BYTES;
  }
}

步骤3:通过配置设置

# In openclaw.config.yaml or openclaw.config.js
media:
  stagingMaxBytes: 26214400  # 25MB

# Or via environment variable
export OPENCLAW_MEDIA_STAGING_MAX_BYTES=26214400

解决方案3:临时运行时覆盖(立即解决方案)

如果您无法直接修改包,请在媒体操作之前应用 monkey-patch:

// patch-media-limit.js
const originalRequire = module.constructor.prototype.require;

module.constructor.prototype.require = function(id) {
  const module = originalRequire.apply(this, arguments);

  if (id === './store' || id.includes('store-CA7OW')) {
    if (module.MediaStore) {
      const OriginalMediaStore = module.MediaStore;
      module.MediaStore = class PatchedMediaStore extends OriginalMediaStore {
        validateSize(...args) {
          // Override staging limit for this session
          const originalLimit = this.constructor.MEDIA_MAX_BYTES;
          try {
            this.constructor.MEDIA_MAX_BYTES = process.env.OPENCLAW_STAGING_MAX_BYTES || 52428800;
            return super.validateSize(...args);
          } finally {
            this.constructor.MEDIA_MAX_BYTES = originalLimit;
          }
        }
      };
    }
  }
  return module;
};

// Load before OpenClaw initialization
require('./patch-media-limit');
const { OpenClaw } = require('openclaw');

🧪 验证

应用修复后,通过以下验证步骤确认问题已解决:

前提条件

# Ensure OpenClaw version includes the fix or workaround
openclaw --version
# Expected: 2026.4.12+ (or patched 2026.4.11)

# Verify configuration is set correctly
openclaw config get media.stagingMaxBytes
# Expected: > 5242880 (if explicitly set)

openclaw config get channels[0].media.maxBytes
# Expected: Your configured channel limit (e.g., 26214400 for 25MB)

验证测试:暂存超过原始5MB限制的媒体

# Create a test file exceeding 5MB (6MB)
dd if=/dev/urandom of=/tmp/test-media-6mb.bin bs=1M count=6

# Attempt to stage through OpenClaw media pipeline
node -e "
const { MediaStore } = require('openclaw/dist/store-CA7OW_2w');
const store = new MediaStore();
const fs = require('fs');

try {
  const buffer = fs.readFileSync('/tmp/test-media-6mb.bin');
  store.validateSize(buffer);
  console.log('SUCCESS: Media staged without error');
  console.log('Size validated:', buffer.length, 'bytes');
} catch (err) {
  console.error('FAILED:', err.message);
  process.exit(1);
}
"

# Expected output after fix:
# SUCCESS: Media staged without error
# Size validated: 6291456 bytes

验证测试:视频生成工作流

# Run a video generation that produces > 5MB output
openclaw run \
  --tool video-generate \
  --prompt "Generate a 45-second animated landscape" \
  --output ./output/video.mp4

# Expected after fix:
# - Command completes without "Media exceeds 5MB limit" error
# - Output file exists and has expected size
ls -lh ./output/video.mp4
# Expected: file size > 5MB if generated successfully

验证测试:限制层级解析

# Test that effective limit respects the minimum of all configured limits
node -e "
const { MediaStore } = require('openclaw/dist/store-CA7OW_2w');
const store = new MediaStore({
  config: {
    media: { stagingMaxBytes: 10485760 }  // 10MB
  },
  channel: {
    media: { maxBytes: 5242880 }  // 5MB
  },
  agent: {
    mediaLimits: { maxBytes: 15728640 }  // 15MB
  }
});

const context = store.getEffectiveLimit({
  config: store._config,
  channel: store._config.channel,
  agent: store._config.agent
});

console.log('Effective limit:', context, 'bytes');
console.log('Expected: 5242880 (minimum of channel: 5MB)');
console.log('Result:', context === 5242880 ? 'PASS' : 'FAIL');
"

验证测试:错误消息清晰度

# Test that error messages now indicate the effective limit source
node -e "
const { MediaStore, MediaLimitError } = require('openclaw/dist/store-CA7OW_2w');
const store = new MediaStore({ config: { media: { stagingMaxBytes: 1000000 } } });
const buffer = Buffer.alloc(2000000); // 2MB

try {
  store.validateSize(buffer);
} catch (err) {
  console.log('Error message:', err.message);
  // Expected: includes both size and effective limit
  // Should NOT just say 'Media exceeds 5MB limit'
}
"

# Expected: Error includes resolved effective limit, not hardcoded 5MB

⚠️ 常见陷阱

环境特定的陷阱

  • Docker/容器部署dist/store-CA7OW_2w.js 中的打包运行时嵌入在容器镜像中。在运行中的容器内修改 node_modules 不会在重启后保留。请始终重建镜像或挂载配置覆盖。
    # Incorrect - changes lost after container restart
    docker exec openclaw-container sed -i 's/5 \* 1024 \* 1024/50 * 1024 * 1024/g' /usr/local/lib/node_modules/openclaw/dist/store-CA7OW_2w.js
    

    Correct - use environment variables or volume mounts

    docker run -e OPENCLAW_MEDIA_STAGING_MAX_BYTES=52428800 openclaw/app

  • npm 全局安装 vs. 本地安装 — 包的位置不同:
    # Global install location
    $(npm root -g)/openclaw/dist/store-CA7OW_2w.js
    

    Local install location

    ./node_modules/openclaw/dist/store-CA7OW_2w.js

    修补错误的位置会导致修复看起来无效。

  • Monorepo/工作区配置 — 当 OpenClaw 安装在工作区根目录时,嵌套的包可能会解析到不同的包实例。确保所有包引用相同的 OpenClaw 安装。
    # Check bundle resolution
    node -e "console.log(require.resolve('openclaw/dist/store-CA7OW_2w.js'))"

配置边缘情况

  • 零值或空值限制 — 修复必须过滤掉非正值:
    # This should NOT be treated as unlimited
    channels:
      slack:
        media:
          maxBytes: 0  # Disables limit? Or error?
    

    Verify your fix handles this correctly

    openclaw config validate

    Should warn about invalid zero/null media limits

  • 通道特定限制 vs. 全局默认值 — 所有限制的最小值通常是有效的正确限制,但某些工作流可能意图使用最大值。请清楚地记录所选语义。
  • 旧版配置文件 — 旧的 .openclawrc JSON 文件可能使用 mediaMaxBytes 而不是 media.maxBytes。验证向后兼容性。

运行时行为陷阱

  • 更新后缓存的包 — 更新 OpenClaw 后,清除模块缓存:
    # Clear require cache in Node.js
    delete require.cache[require.resolve('openclaw/dist/store-CA7OW_2w.js')]
    

    Or restart the Node.js process entirely

  • 未缓冲的流式媒体 — 如果媒体是流式的(例如视频生成),大小验证可能需要先读取整个流。确保修复同时处理缓冲和流式场景。
  • 大限制下的内存压力 — 设置非常高的暂存限制(例如500MB+)而没有相应的内存配置可能会导致暂存操作期间的 OOM 错误。

验证失败

如果验证测试意外失败:

  1. 确认包确实已更新(md5sum dist/store-CA7OW_2w.js
  2. 检查模块解析路径中是否有多个 OpenClaw 安装
  3. 验证环境变量是否正确读取(console.log(process.env.OPENCLAW_MEDIA_STAGING_MAX_BYTES)
  4. 确保进程有权读取更新的包文件

🔗 相关错误

以下错误和问题在上下文上与硬编码的5MB暂存限制相关:

  • MediaLimitError — 当媒体超过暂存阈值时抛出的主要错误类。可能显示为"Media exceeds 5MB limit",或者在修复后显示为"Media exceeds {effectiveLimit} bytes"。
  • MEDIA_EXCEEDS_CHANNEL_LIMIT — 在暂存后如果通道特定限制仍然超出时发生的下游错误。如果暂存限制正确地从通道配置派生,则不应出现此错误。
  • ENFILE / EMFILE(文件描述符限制) — 不是直接相关,但当大媒体文件处理不当而没有适当的流式处理时可能会出现。经常与大小限制错误混淆。
  • EBADMSG(消息不可处理) — 如果媒体验证由于大小约束而在完全解析之前部分失败,可能会发生。5MB限制可能导致不完整的媒体对象被传递到下游。
  • 问题:媒体元数据的静默截断 — 相关的上游问题,其中当大小接近暂存限制时媒体元数据被剥离,导致下游交付失败,错误消息不明确。
  • 问题:持续时间 > ~15秒的视频生成失败 — 用户报告说较长时间的视频生成失败,可能是因为累积的帧数据在视频完全组装之前就超过了5MB的暂存上限。
  • 问题:音频文件的元数据被剥离 — 与媒体暂存行为相关,其中高比特率音频文件被部分处理,在大小验证严格时丢失ID3/元数据标签。
  • Upload payload too large — 来自下游集成(Slack、Discord等)的通道特定错误,表示实际超出了平台的限制。如果此错误在暂存修复后出现,则需要调整通道限制本身。
  • BufferOverflowError(自定义) — 如果暂存层被扩展而没有相应的缓冲区大小调整,可能会在自定义 OpenClaw 部署中出现。
  • 历史记录:dist/store.js 重命名为 dist/store-CA7OW_2w.js — 包文件名在版本之间发生了变化。确保文档引用的是已安装版本的正确当前包名称。

交叉引用矩阵

错误/问题层级限制来源可覆盖暂存?
Media exceeds 5MB limitStaging (bundled)Hardcoded constantNo (requires fix)
MEDIA_EXCEEDS_CHANNEL_LIMITChannel deliverychannel.media.maxBytesYes (config)
Upload payload too largeExternal APIPlatform limitNo
BufferOverflowErrorCustom/ExtendedCustom implementationDepends

依据与来源

本故障排除指南由 FixClaw 智能管线从社区讨论中自动合成。