Skip to content

[Bug]: Re-embedding an overwritten local image shows the old cached version #15676

Open

Description

@btsouth

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

No open issue covers this. #13942 and #14009 addressed the same problem and were closed under the prior-approval rule, so this issue is for maintainers to decide the intended behavior.

Area

apps/server, apps/web

Actual behavior

An agent regenerated an image by overwriting the same PNG it had embedded earlier in the thread, then embedded that path again in its reply. The new message showed the old image. Opening the same file from the files panel showed the correct, updated image, so the two views disagreed side by side.

The file on disk was correct, and the agent had read the new file back before replying.

Steps to reproduce

  1. Have an agent write an image to a path, for example /tmp/chart.png, and embed it in a reply with ![chart](/tmp/chart.png).
  2. Within an hour, have it overwrite that file with different content and embed the same path in a new reply.
  3. The new reply shows the previous image.
  4. Open the file from the files panel. It shows the current image.

Expected behavior

A message that embeds a local image shows the file as it was when the message was written, or at least never shows an older version than the one on disk.

Cause

Two caches key only on the path:

  • The client reuses the signed asset URL per resource path for up to an hour (useAssetUrlState, ASSET_URL_IDLE_TTL_MS in packages/client-runtime/src/state/assets.ts).
  • The server serves images with Cache-Control: private, max-age=3600 (assetResponseHeaders in apps/server/src/http.ts). The token does not include the file's mtime or size.

Audio and video already get no-store because "host media can change in place", but images do not.

Confirmed on main at 4ee6bfd5.

Open question

The closed PRs show the possible directions:

  1. Treat local images as live references and revalidate them (for example no-cache with an mtime/size ETag). Small change, but older messages would also switch to the newest file.
  2. Snapshot referenced images per message (fix: chat images keep the version from when the message was written #14009). Preserves history, but adds storage and retention.
  3. Prompt-only guidance to write revisions to new file names (fix(server): tell agents to write image revisions to new file names #13942).

Which behavior is intended?

Impact

Minor bug. Misleading when an agent iterates on an image or screenshot, because the chat shows a result that does not match the file.

Version or commit

0.0.46-nightly.20261004.2648. Cause confirmed on main at 4ee6bfd5.

Environment

Linux, T3 desktop app, Claude provider.

Workaround

Reload the window, open the file from the files panel, or have the agent write each revision to a new file name.

Activity

  1. juliusmarminge commented on Oct 4, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the write-up, @btsouth, and for linking the two closed PRs, which give useful context.

    What I found

    Confirmed on main at 4ee6bfd50e.

    • Chat reuses a cached URL. Chat embeds a local image as a media-file resource and renders whatever useAssetUrlState already has (ChatMarkdownAssetImage in apps/web/src/components/ChatMarkdown.tsx). That cache is keyed by the resource, not by the file's contents.
      • While the thread stays open, the signed URL refreshes every 30 minutes.
      • A new subscriber doesn't refetch during the 5-minute stale window.
      • An unused entry lives for an hour.
      • The timings are set in packages/client-runtime/src/state/assets.ts, and mobile uses the same hook.
    • The token doesn't track content. The signed claims are path, device, and inode, plus a one-hour expiry (finalizeAbsoluteMediaFileAsset in apps/server/src/assets/AssetAccess.ts). Overwriting a file in place keeps its inode, and the token carries no mtime or size. The old pixels come from reusing the URL.
    • Images are cacheable, unlike other media. Images get Cache-Control: private, max-age=3600 (assetResponseHeaders in apps/server/src/http.ts), while audio and video are rewritten to private, no-store in assetFileResponse.
    • Why the files panel shows the right image. WorkspaceImagePreview signs a workspace-file URL with a workspace-revision taken from the latest file change, command, or checkpoint (FilePreviewPanel.tsx, workspaceMutationId in ChatView.tsx). That skips both caches, and reloading has the same effect, which matches your workaround.
    • Files outside the project. Absolute paths like /tmp/chart.png still go stale in chat, but the files panel only resolves files inside the workspace root.

    Likely fix area

    The options line up with the ones you listed. Picking one is a decision about what an image path in a message should mean over time:

    A maintainer will decide on the fix direction.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions