openclaw/openclaw · error · Error

Diff viewer HTML exceeds ${MAX_DECODED_HTML_BYTES} bytes.

Error message

Diff viewer HTML exceeds ${MAX_DECODED_HTML_BYTES} bytes.

What it means

Thrown by DiffArtifactStore.createArtifact when the UTF-8 byte length of the rendered viewer HTML exceeds MAX_DECODED_HTML_BYTES (64 MiB). This caps the in-memory artifact size before gzip compression and blob storage; the same constant bounds gunzip on read (1371 is checked at write time). A viewer exceeding 64MB decoded would exhaust memory and never load in a browser.

Source

Thrown at extensions/diffs/src/store.ts:97

  constructor(params: {
    rootDir: string;
    blobStore: PluginBlobStore<DiffArtifactBlobMetadata>;
    logger?: PluginLogger;
    cleanupIntervalMs?: number;
  }) {
    this.rootDir = path.resolve(params.rootDir);
    this.blobStore = params.blobStore;
    this.logger = params.logger;
    this.cleanupIntervalMs =
      params.cleanupIntervalMs === undefined
        ? DEFAULT_CLEANUP_INTERVAL_MS
        : Math.max(0, Math.floor(params.cleanupIntervalMs));
  }

  async createArtifact(params: CreateArtifactParams): Promise<DiffArtifactMeta> {
    const html = Buffer.from(params.html, "utf8");
    if (html.byteLength > MAX_DECODED_HTML_BYTES) {
      throw new Error(`Diff viewer HTML exceeds ${MAX_DECODED_HTML_BYTES} bytes.`);
    }
    const compressedHtml = await gzipAsync(html);
    const token = crypto.randomBytes(24).toString("hex");
    const ttlMs = normalizeTtlMs(params.ttlMs);
    const metadata: DiffViewerArtifactMetadata = {
      version: 1,
      kind: "viewer",
      encoding: "gzip",
      tokenHash: hashToken(token),
      title: params.title,
      inputKind: params.inputKind,
      fileCount: params.fileCount,
      decodedBytes: html.byteLength,
      ...(params.context ? { context: params.context } : {}),
    };
    const entry = await this.registerUnique(compressedHtml, metadata, ttlMs);
    this.scheduleCleanup();
    return viewerEntryToMeta(entry, token);

View on GitHub (pinned to 01804a7531)

Solutions

  1. Reduce the diff content that produced the HTML (fewer files, fewer lines, collapse unchanged context).
  2. Split into multiple smaller artifacts.
  3. If before/after, diff a smaller region of the file.
  4. Verify the render target isn't producing both viewer+image HTML redundantly for the same oversized content.

Example fix

// before: one artifact for a giant diff
await store.createArtifact({ html: hugeRenderedHtml, ... });

// after: chunk files into multiple artifacts under the limit
for (const chunk of chunkFiles(files)) {
  await store.createArtifact({ html: render(chunk), ... });
}
Defensive patterns

Strategy: validation

Validate before calling

import { Buffer } from "node:buffer";
const MAX = 64 * 1024 * 1024;
if (Buffer.byteLength(html, "utf8") > MAX) {
  throw new Error(`HTML ${Buffer.byteLength(html)} bytes exceeds ${MAX}`);
}

Prevention

When it happens

Trigger: Calling createArtifact (via the diffs tool's artifact rendering path) with HTML whose Buffer.byteLength('utf8') > 64*1024*1024. This follows the patch size guards — a huge patch that passed file-count/line limits but still produced oversized HTML, or a pathological before/after of a very large file.

Common situations: A diff just under the 120k-line cap but with heavy syntax highlighting/side-by-side doubling the HTML; a single enormous file in before/after mode; many files each adding navigation/prerender overhead. Typically means upstream limits were skirted.

Related errors


AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12). Data as JSON: /api/errors/3fb381baa9f9c462. Report an issue: GitHub.