Skip to content

Signal image captions truncated to first character (Infinity chunk limit normalizes to 1) #92734

Description

@yhterrance

Summary

Sending an image with a caption on Signal delivers only the first character of the caption (e.g. "Here's another photo of Ethan…" arrives as "H"). Plain text messages are unaffected.

Root cause

renderMarkdownIRChunksWithinLimit (packages/markdown-core/src/render-aware-chunking.ts) normalizes its limit through resolveIntegerOption(limit, 1, { min: 1 }), which rejects any non-finite value and falls back to 1:

function resolveIntegerOption(value, fallback, opts) {
  if (!Number.isFinite(value)) return fallback; // Infinity -> 1
  return Math.max(opts.min, Math.trunc(value));
}
const normalizedLimit = resolveIntegerOption(options.limit, 1, { min: 1 });

The Signal media path passes Number.POSITIVE_INFINITY to mean "no size cap" and then takes [0] (extensions/signal/src/channel.ts, sendFormattedSignalMedia):

const formatted = markdownToSignalTextChunks(ctx.text, Number.POSITIVE_INFINITY, { tableMode })[0] ?? { ... };

With the limit collapsed to 1, the caption is chunked into one chunk per character, and [0] is the first character. The Signal text path (sendFormattedSignalText) has the same latent issue when the configured chunk limit is disabled.

Minimal repro

import { markdownToIR } from "openclaw/packages/markdown-core/src/ir.js";
import { renderMarkdownIRChunksWithinLimit } from "openclaw/packages/markdown-core/src/render-aware-chunking.js";

const ir = markdownToIR("hello world");
const chunks = renderMarkdownIRChunksWithinLimit({
  ir,
  limit: Number.POSITIVE_INFINITY,
  renderChunk: (c) => c.text,
  measureRendered: (r) => r.length,
});
// expected: 1 chunk "hello world"
// actual:   11 chunks, chunks[0].source.text === "h"

Impact / scope

  • Not model-specific — reproduces by calling the chunker directly, no agent involved.
  • Affects every Signal image-with-caption send.
  • Present on main and in published 2026.6.1, 2026.6.6 (latest), and 2026.6.7-beta.1 (verified the identical code in each tarball).

Fix

PR incoming: treat an Infinity limit as "no cap" (emit the whole IR as a single chunk). NaN/other invalid limits keep the existing fallback; finite limits still split.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions