Skip to content

fix(media/image): surface sharp-install hint when optimizer exhausts every resize attempt#77197

Open
lonexreb wants to merge 2 commits intoopenclaw:mainfrom
lonexreb:fix/73148-image-sharp-missing-hint
Open

fix(media/image): surface sharp-install hint when optimizer exhausts every resize attempt#77197
lonexreb wants to merge 2 commits intoopenclaw:mainfrom
lonexreb:fix/73148-image-sharp-missing-hint

Conversation

@lonexreb
Copy link
Copy Markdown
Contributor

@lonexreb lonexreb commented May 4, 2026

Summary

When optimizeImageToJpeg exhausts every (size, quality) attempt because the optional sharp native dep is missing, the throw used to read

Failed to optimize image: Cannot find module 'sharp' imported from .../image-ops.js

with no install hint. On hosts where the global install skipped the native build (e.g. the reporter's npm i -g openclaw on Ubuntu), every image-tool call returned the opaque Failed to optimize image and operators had to guess what to install.

isOptionalImageOptimizerUnavailable already walks the cause chain and detects this exact failure shape — it's the existing safety net that lets optimizeAndClampImage fall back to the original buffer when it's already under cap. Reuse it at the bottom of the resize loop: when the loop's firstResizeError matches that detector, throw a clear, actionable message that names the missing dep and the install command.

Why this is the right place

There are two existing graceful-fallback paths above this throw:

  1. optimizeAndClampImage returns the original buffer when sharp is missing and the buffer is already under cap and the source isn't HEIC.
  2. optimizeImageToJpeg's HEIC branch wraps with HEIC image conversion failed: and includes the inner message via String(err).

Neither catches the case the issue reports: the agent image tool calls into optimizeImageToJpeg (via tool-images.ts:resizeToJpeg) for over-cap or non-HEIC images that need actual resizing — those exhaust the resize loop with sharp module-resolution errors and hit the bottom throw. That's exactly where this fix sits.

Changes

  • src/media/web-media.ts — At the bottom of optimizeImageToJpeg, if isOptionalImageOptimizerUnavailable(firstResizeError) matches, throw a sharp-install-hint message that names the install command. Cause is preserved so log inspectors can still see the underlying Cannot find module 'sharp'.
  • src/media/web-media.test.ts — Add a regression test (#73148) that calls optimizeImageToJpeg directly with the existing withUnavailableImageOptimizer mock and asserts the new hint text. Relax two existing tests that asserted on the old literal /Optional dependency sharp is required/ to /sharp/i so both the old (cause-chain) and new (top-level) wording are accepted — the underlying behavior they cover hasn't changed.
  • CHANGELOG.md — Unreleased > Fixes entry credited to me.

Test plan

  • pnpm test src/media/web-media.test.ts — 39/39 pass (1 new + 38 existing)
  • pnpm exec oxfmt --check on touched files — clean

Fixes #73148.

Real behavior proof

Test: pnpm test src/media/web-media.test.ts — 39 passed.

Calls optimizeImageToJpeg against a missing-sharp scenario and asserts the thrown error contains both the local repo (pnpm add sharp) and global install (npm install -g openclaw@latest --include=optional) repair commands. Same code path that fires when an embedded reply tries to resize a JPEG on a host without the optional native dep.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 4, 2026

Codex review: needs real behavior proof before merge.

Summary
The PR adds a missing-sharp diagnostic branch in optimizeImageToJpeg, a regression test, and a changelog entry for the image optimizer error path.

Reproducibility: yes. Source inspection shows current main catches each resizeToJpeg failure and then throws the generic resize-loop error; the PR's mock test exercises the same missing-sharp path, though I did not run a live global install in this read-only review.

Real behavior proof
Needs real behavior proof before merge: The PR body only provides mocked unit test output, not after-fix output from a real missing-sharp setup.

Next step before merge
Contributor action is needed because the PR lacks real behavior proof and the error copy still needs policy-aligned global guidance.

Security
Cleared: The diff changes error text, a unit test, and changelog only; it does not alter dependency sources, install scripts, permissions, publishing, or secret handling.

Review findings

  • [P2] Use a global sharp command that actually installs sharp — src/media/web-media.ts:717-718
Review details

Best possible solution:

Land a diagnostic-only fix after the message names a global repair that current package resolution/dependency policy actually supports and after the contributor supplies real missing-sharp proof.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main catches each resizeToJpeg failure and then throws the generic resize-loop error; the PR's mock test exercises the same missing-sharp path, though I did not run a live global install in this read-only review.

Is this the best way to solve the issue?

No, not as written. The detector branch is the narrow maintainable fix, but the global install guidance should either use a command that actually places sharp on the global OpenClaw runtime path or deliberately change package dependency policy.

Full review comments:

  • [P2] Use a global sharp command that actually installs sharp — src/media/web-media.ts:717-718
    The message still presents npm install -g openclaw@latest --include=optional as the global repair path, but sharp is not a root optional dependency and packaged postinstall does not install plugin package dependencies. A global user can follow that primary command and still restart into the same missing-sharp failure; make the hint use a path-aware sharp install command or update the package dependency policy deliberately.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.88

What I checked:

  • Current main failure path: Current main records resize errors, then falls through to Failed to optimize image... after all resize attempts fail, so the opaque missing-optimizer behavior is still present on main. (src/media/web-media.ts:703, 7d5ca3064a51)
  • Existing missing-sharp detector: isOptionalImageOptimizerUnavailable already walks the error cause chain and recognizes missing sharp package/module messages, so the PR's branch placement is consistent with current source structure. (src/media/web-media.ts:208, 7d5ca3064a51)
  • Sharp import failure shape: The media-understanding runtime dynamically imports sharp and wraps import failures as Optional dependency sharp is required for image attachment processing, preserving the cause. (extensions/media-understanding-core/image-ops.ts:43, 7d5ca3064a51)
  • Global reinstall hint is not backed by package metadata: The root package declares only sqlite-vec in optionalDependencies; sharp is declared in the media-understanding plugin package, so npm install -g openclaw@latest --include=optional is not shown by current metadata to install sharp. (package.json:1751, 7d5ca3064a51)
  • Postinstall policy avoids plugin dependency install: Packaged postinstall states plugin package dependencies are installed only by explicit plugin install/update flows, never by postinstall; the runtime dependency docs similarly require actionable errors instead of startup/package-manager mutation. (scripts/postinstall-bundled-plugins.mjs:3, 7d5ca3064a51)
  • PR proof remains mock-only: The PR body's Real behavior proof section reports pnpm test src/media/web-media.test.ts against a mocked unavailable optimizer; it does not include terminal/log output from a real missing-sharp global install after the fix. (bc4c1c5ddf67)

Likely related people:

  • vincentkoc: Recent merged history moved Sharp-backed image ops into the media runtime and maintained adjacent media optimizer and image input behavior. (role: recent media/runtime owner; confidence: high; commits: e174d96cc0b2, 3dcff3b267b0, 0ed4f8a72bb1; files: src/media/web-media.ts, src/media/image-ops.ts, extensions/media-understanding-core/image-ops.ts)

Remaining risk / open question:

  • The PR still lacks after-fix proof from a real missing-sharp global install; mocked unit tests are supplemental only.
  • The primary global reinstall hint is not backed by current root optional dependency metadata or packaged postinstall behavior unless the dependency policy changes.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 7d5ca3064a51.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b561df525a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/media/web-media.ts Outdated
if (isOptionalImageOptimizerUnavailable(firstResizeError)) {
throw new Error(
"Image optimization requires the optional `sharp` native dependency, which is not installed. " +
"Install it with `pnpm add sharp` (or `npm install sharp` / `bun add sharp`) and restart the gateway.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provide global-aware sharp install guidance

This hint is shown specifically for hosts where a global OpenClaw install is missing sharp, but the suggested commands (pnpm add sharp / npm install sharp / bun add sharp) are local installs, so operators can follow the message and still hit the same error on restart because the global runtime import path is unchanged. The remediation should include a global/path-aware command (for example the docs’ global reinstall flow) so the advice actually resolves the reported scenario.

Useful? React with 👍 / 👎.

@lonexreb lonexreb force-pushed the fix/73148-image-sharp-missing-hint branch from b561df5 to fd35b2b Compare May 4, 2026 09:43
lonexreb added a commit to lonexreb/paloa-claw that referenced this pull request May 4, 2026
Codex review on PR openclaw#77197 noted: the previous hint suggested local install
commands (pnpm add sharp / npm install sharp / bun add sharp) but the
scenario operators most often hit is a global `npm install -g openclaw`
where the global runtime resolves `sharp` from openclaw's own global
node_modules, so a workspace install does not actually fix the failure on
restart.

Expand the thrown error to include both repair postures: the existing local
commands plus an explicit global path (`npm install -g openclaw@latest
--include=optional` or `npm install -g sharp`) so operators on either
install posture get a working remediation.

Update the regression test to assert both surfaces appear in the message.
@lonexreb lonexreb force-pushed the fix/73148-image-sharp-missing-hint branch from fd35b2b to b969371 Compare May 4, 2026 19:30
@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 4, 2026

Addressed [P2] from codex review (src/media/web-media.ts:712). Expanded the thrown error to cover both repair postures so operators on either install layout get a working remediation:

  • Local repo: pnpm add sharp / npm install sharp / bun add sharp (preserved from original fix).
  • Global install (npm install -g openclaw): npm install -g openclaw@latest --include=optional (or npm install -g sharp as a fallback when openclaw cannot be reinstalled). The previous local-only commands were inert here because the global runtime resolves sharp from the global openclaw node_modules.

Updated the existing regression test to assert both surfaces appear in the message. Targeted tests still green: src/media/web-media.test.ts 39/39. Also rebased onto current origin/main.

lonexreb added 2 commits May 5, 2026 13:12
…every resize attempt (openclaw#73148)

When optimizeImageToJpeg's resize loop fails on every (size, quality)
combination because the optional native dep "sharp" is missing, the
throw used to read 'Failed to optimize image: Cannot find module
sharp...' with no install hint, leaving operators on globally-installed
hosts (where pnpm/npm skipped the native build) guessing.

isOptionalImageOptimizerUnavailable already detects the cause-chain
shape; reuse it. When it matches, throw a clear 'Image optimization
requires the optional sharp native dependency' message that names the
install command. Cause is preserved.

Tests: new direct-call regression in web-media.test.ts asserts the
new hint text. Two existing 'sharp unavailable' tests relaxed from
the literal old prefix to /sharp/i so both old (cause-chain) and new
(top-level) wording are accepted.
Codex review on PR openclaw#77197 noted: the previous hint suggested local install
commands (pnpm add sharp / npm install sharp / bun add sharp) but the
scenario operators most often hit is a global `npm install -g openclaw`
where the global runtime resolves `sharp` from openclaw's own global
node_modules, so a workspace install does not actually fix the failure on
restart.

Expand the thrown error to include both repair postures: the existing local
commands plus an explicit global path (`npm install -g openclaw@latest
--include=optional` or `npm install -g sharp`) so operators on either
install posture get a working remediation.

Update the regression test to assert both surfaces appear in the message.
@lonexreb lonexreb force-pushed the fix/73148-image-sharp-missing-hint branch from b969371 to bc4c1c5 Compare May 5, 2026 18:12
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Image tool: opaque "Failed to optimize image" when sharp is not installed

1 participant