vxcontrol/pentagi · error
failed to put terminal log (edit file cmd): %w
Error message
failed to put terminal log (edit file cmd): %w
What it means
After a successful edit, EditFile logs a styled success line to the terminal log via t.tlp.PutMsg (termlog stdin entry). This error wraps that logging call failing — the edit itself succeeded and the file is already updated, but recording the action in the termlog store failed. It typically indicates a database/persistence problem in the terminal log pipeline rather than anything wrong with the file.
Source
Thrown at backend/pkg/tools/terminal.go:527
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
// volume garbage collector force-removes anything matching
// HasPrefix("pentagi-terminal-") && HasSuffix("-data"), so a leading tenant
// segment keeps one tenant's objects outside another tenant's sweep. A tenant
// segment placed after the prefix would stay inside it and be destroyed.
func PrimaryTerminalName(tenantPrefix string, flowID int64) string {
return fmt.Sprintf("%s%s%d", tenantPrefix, PrimaryTerminalNamePrefix, flowID)View on GitHub (pinned to ea665308ba)
Solutions
- Check backend logs and Postgres health (connection count, `SELECT 1`) for the underlying cause
- Retry the EditFile action only if you must see the termlog entry — the file edit already took effect, so re-running will apply a diff against already-edited content; prefer verifying with ReadFile instead
- Increase DB connection pool limits / fix connectivity if pool exhaustion is the cause
- Consider degrading gracefully: treat termlog write failure as non-fatal so file edits are not reported as errors
- Use a non-canceled context (the edit already succeeded, so an upstream cancellation shouldn't fail the whole call)
Example fix
// before
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)
}
// after
logCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
if _, err := t.tlp.PutMsg(logCtx, database.TermlogTypeStdin, styledMsg, t.containerID, t.taskID, t.subtaskID); err != nil {
cancel()
t.logger.Warn().Err(err).Str("path", path).Msg("edit succeeded but termlog write failed")
return successMsg, nil // file edit already applied; don't fail the tool call
}
cancel() Defensive patterns
Strategy: try-catch
Try / catch
msg, err := term.EditFile(ctx, flowID, path, diff)
if err != nil && strings.Contains(err.Error(), "failed to put terminal log") {
// The file edit already succeeded; verify with ReadFile instead of retrying the edit.
current, rerr := term.ReadFile(ctx, flowID, "", path)
_ = current; _ = rerr
} Prevention
- Remember the edit is already applied — never blindly re-run the same diff after this error
- Monitor Postgres connectivity and pool saturation for the termlog store
- Use non-cancelable contexts for post-action logging writes
- Treat termlog failures as observability degradation, not data loss of the file edit
When it happens
Trigger: PutMsg failing because the PostgreSQL termlog insert errored: DB down, connection pool exhausted, context canceled/deadline exceeded mid-insert, or a failed transaction.
Common situations: Database restart or network blip between backend and Postgres during a flow; context timeout from an upstream request canceled by the client while the write was already committed; pool saturation under heavy agent concurrency.
Related errors
- failed to create flow agent log: %w
- failed to create flow msg log: %w
- failed to get flow agent log: %w
- failed to put input for task %d: %w
- failed to put terminal log (stdin): %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/32ddf9ae6c8ed514.
Report an issue: GitHub.