vxcontrol/pentagi · error

failed to write edited content of %s: %w

Error message

failed to write edited content of %s: %w

What it means

Once the diff applied, EditFile writes the new content back via writeFileToContainer. This error wraps any failure from that write path — practically it surfaces the CopyToContainer failure (missing parent directory, container not running, permissions) since the tar-generation errors are rare. Importantly, the edit is then incomplete: the diff applied in memory but persistence failed, so the on-disk file still holds the old content.

Source

Thrown at backend/pkg/tools/terminal.go:521

	if path == "" {
		return "", fmt.Errorf("path is required and cannot be empty")
	}
	if strings.TrimSpace(diffText) == "" {
		return "", fmt.Errorf("diff is required and cannot be empty")
	}

	current, err := t.readFileFromContainer(ctx, flowID, path)
	if err != nil {
		return "", fmt.Errorf("failed to read current content of %s before editing: %w", path, err)
	}

	newContent, hunksApplied, err := ApplyUnifiedDiff(current, diffText)
	if err != nil {
		return "", fmt.Errorf("failed to apply diff to %s: %w", path, err)
	}

	if err := t.writeFileToContainer(ctx, flowID, path, newContent); err != nil {
		return "", fmt.Errorf("failed to write edited content of %s: %w", path, err)
	}

	successMsg := fmt.Sprintf("Applied %d diff hunk(s) to %s (%d -> %d bytes)", hunksApplied, path, len(current), len(newContent))
	styledMsg := fmt.Sprintf("%s%s%s%s", ansiColorSystemMsg, successMsg, ansiColorReset, ansiLineTerminator)
	if _, err := t.tlp.PutMsg(ctx, database.TermlogTypeStdin, styledMsg, t.containerID, t.taskID, t.subtaskID); err != nil {
		return "", fmt.Errorf("failed to put terminal log (edit file cmd): %w", err)
	}

	return successMsg, nil
}

// PrimaryTerminalName returns the docker container name for a flow's primary
// terminal, namespaced by the configured tenant.
//
//	"pentagi-terminal-1"       (single instance)
//	"acme-pentagi-terminal-1"  (TENANT_ID=acme)
//
// The tenant goes in FRONT of the well-known prefix on purpose: the installer's

View on GitHub (pinned to ea665308ba)

Solutions

  1. Read the wrapped %w cause and fix accordingly: 'no such directory' → mkdir -p first; 'not running' → restart the flow container; 'permission denied' → choose a writable path or fix the mount
  2. Check container disk space (docker system df, df inside container) if the cause indicates a write error
  3. Retry EditFile after confirming the container is running and the directory exists
  4. If diffs keep failing to persist, switch to WriteFile with the full content as a fallback
  5. Avoid editing files on read-only or volatile mounts; persist data under a volume-backed path

Example fix

// before
if err := t.writeFileToContainer(ctx, flowID, path, newContent); err != nil {
    return "", fmt.Errorf("failed to write edited content of %s: %w", path, err)
}
// after
if err := t.writeFileToContainer(ctx, flowID, path, newContent); err != nil {
    if mkErr := t.ensureDirExists(ctx, containerName, filepath.Dir(path)); mkErr == nil {
        if retryErr := t.writeFileToContainer(ctx, flowID, path, newContent); retryErr == nil {
            return successMsg, nil
        }
    }
    return "", fmt.Errorf("failed to write edited content of %s: %w", path, err)
}
Defensive patterns

Strategy: retry

Validate before calling

out, _ := term.ExecuteCommand(ctx, flowID, fmt.Sprintf("test -d %s -a -w %s && echo OK", filepath.Dir(path), filepath.Dir(path)))
if !strings.Contains(out, "OK") {
    term.ExecuteCommand(ctx, flowID, fmt.Sprintf("mkdir -p %s", filepath.Dir(path)))
}

Try / catch

msg, err := term.EditFile(ctx, flowID, path, diff)
if err != nil && strings.Contains(err.Error(), "failed to write edited content") {
    time.Sleep(2 * time.Second) // container may have been restarting
    msg, err = term.EditFile(ctx, flowID, path, diff) // re-reads fresh content
}

Prevention

When it happens

Trigger: Writing back to a path whose parent directory disappeared or was never a directory in the container; the container stopped between the read and the write; read-only mount or permission denied on the target path; Docker daemon connection failure during CopyToContainer.

Common situations: Agent editing files under a directory that a prior cleanup step removed; container restarted (fresh filesystem) between read and write on a long-running edit; writing into /proc, /sys, or other read-only locations; disk-full on the container's writable layer.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/9f2246c241eb93cf. Report an issue: GitHub.