Skip to content

[Bug]: create_file reports "Created … (N chars)" without verifying the write, so a failed write reports success #15920

Description

@Odrec

What happened?

create_file builds its success message purely from the length of the input string. It never checks that the file actually persisted. A write that never landed still reports success, and the model — reasonably — believes it.

packages/api/src/agents/handlers.ts L2944 (current dev):

const summary = `${action} ${filePath} (${content.length} chars).`;

content.length is the input the model supplied. Nothing between this line and the response confirms the file exists.

The same pattern appears at L3582, L3641, L3730 and L3816 — five sites, so a fix probably wants to be a shared helper rather than five edits.

Why this matters

This is the reporting layer on top of the sandbox's per-execution statelessness, and it turns a recoverable situation into a confusing one.

A user reported this to us as "the agent says it created the file but ls doesn't show it, then it writes the file again and reports something different each time." The underlying cause was benign (the file appears on the next call). But because create_file reports success unconditionally, the model has no way to tell that apart from a genuine failure, so it produces contradictory narration and sometimes rewrites the file repeatedly.

We have also seen a genuine infrastructure failure where generated files were silently discarded — the code API pruned every file from the response because it could not reach the file server. In that state create_file still reported "Created … (N chars)" for every write. The success string cannot distinguish "written" from "silently dropped", which is exactly when a user most needs it to.

Related: LibreChat-AI/agents#274 documents create_file returning "Created" while the file is genuinely absent. That fixed the session routing; this issue is that the success string cannot detect such a failure at all.

Suggested fix

Either would help, roughly in order of value:

  1. Verify the write before reporting success — the /exec response already carries files[]; assert the written path appears in it, and report an error if not.
  2. If verification isn't practical on every path, make the message reflect what is actually known — e.g. state that the file is registered and will be available to the next call, rather than asserting it was created.

Steps to reproduce

The clearest reproduction is the infrastructure-failure case, since the benign one still ends with the file present:

  1. Put the code API in a state where generated files cannot be uploaded (e.g. the in-guest API cannot reach the file server; the response then returns files: []).
  2. Ask an agent to create_file /mnt/data/probe.txt.
  3. The tool reports Created /mnt/data/probe.txt (7 chars).
  4. The file is never persisted; it is absent on this and every subsequent call.

Version

v0.8.7 and v0.8.8-rc2; the pattern is unchanged on current dev.

Environment

Self-hosted, Docker, self-hosted code interpreter (ClickHouse/code-interpreter), Anthropic via Google Vertex AI.

Activity

  1. gokay-ai commented on Sep 14, 2026

    @gokay-ai
    Contributor

    Taking this — will verify create_file (and the sibling write paths) against the filesystem before reporting success.

  2. danny-avila commented on Sep 14, 2026

    @danny-avila
    Collaborator

    Thanks for the detailed report. The failure you reproduced, where codeapi couldn't reach the file server and returned files: [] while create_file still said "Created", was fixed on dev by #15671. It will ship in the next release after v0.8.8-rc2. When codeapi prunes files it couldn't upload, it now sets artifact_delivery. create_file and the other sandbox authoring writes then return an error saying the file couldn't be persisted and shouldn't be retried automatically.

    A note on the suggested fix: files[] can't be treated as proof of a write. Codeapi deliberately leaves out unchanged files in session mode, unsupported extensions, and files under hidden directories. So asserting the path is in files[] would flag successful writes as failures (see #15925).

    One gap remains, and it's on the code-interpreter side. A few output limits drop files from files[] without setting any marker: SANDBOX_MAX_OUTPUT_FILES (default 50), nesting depth, file size, path shape, and unreadable files. That's tracked in LibreChat-AI/code-interpreter#198. Closing here since the LibreChat side is fixed; Fixes keywords don't close issues when a PR merges to dev, so we close them by hand.

  3. Odrec commented on Sep 14, 2026

    @Odrec
    ContributorAuthor

    Thanks — and the correction on files[] is well taken. I'd suggested asserting the written path appears in files[] without knowing about the deliberate omissions (unchanged files in session mode, unsupported extensions, hidden directories); that check would indeed have turned successful writes into reported failures. Good that it was caught before it shipped.

    artifact_delivery via #15671 is the right shape, and I've confirmed it's on dev and not in v0.8.8-rc2 — so it reaches us with the release after that, along with the rest.

    LibreChat-AI/code-interpreter#198 captures the part we can still be bitten by, thanks for opening it.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions