vxcontrol/pentagi · error

failed to put terminal log (stdout): %w

Error message

failed to put terminal log (stdout): %w

What it means

After a successful exec, getExecResult logs the styled output to the terminal log store via t.tlp.PutMsg with TermlogTypeStdout. This error wraps a failure of that persistence call, meaning the command ran fine but its transcript could not be recorded, so the function aborts to keep the terminal log consistent.

Source

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

				"For long batch commands, wrap with shell timeout utility: 'timeout %d <command>' to ensure clean completion",
			ctx.Err(),
			truncateString(dst.String(), 500),
			suggestedTimeout,
		)
	}

	// wait for the exec process to finish
	_, err = t.dockerClient.ContainerExecInspect(ctx, id)
	if err != nil {
		return "", fmt.Errorf("failed to inspect exec process: %w", err)
	}

	results := dst.String()
	// Style system output with color coding
	styledOutput := fmt.Sprintf("%s%s%s%s", ansiColorSystemMsg, results, ansiColorReset, ansiLineTerminator)
	_, err = t.tlp.PutMsg(ctx, database.TermlogTypeStdout, styledOutput, t.containerID, t.taskID, t.subtaskID)
	if err != nil {
		return "", fmt.Errorf("failed to put terminal log (stdout): %w", err)
	}

	if results == "" {
		results = "Command completed successfully with exit code 0. No output produced (silent success)"
	}

	return results, nil
}

func (t *terminal) ReadFile(ctx context.Context, flowID int64, path string) (string, error) {
	if path == "" {
		return "", fmt.Errorf("path is required and cannot be empty")
	}

	cwd := docker.WorkFolderPathInContainer
	escapedPath := strings.ReplaceAll(path, "'", "'\"'\"'")
	catCommand := fmt.Sprintf("cat '%s'", escapedPath)
	// Format read file command with styling

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check database connectivity and logs (Postgres health, connection pool settings)
  2. Verify the termlog table constraints/column sizes vs the output size (truncate large outputs before PutMsg)
  3. Sanitize non-UTF8 output before logging
  4. Retry the operation once the DB recovers — the command result itself was valid

Example fix

// before
_, err = t.tlp.PutMsg(ctx, database.TermlogTypeStdout, styledOutput, ...)
if err != nil { return "", fmt.Errorf("failed to put terminal log (stdout): %w", err) }
// after
styledOutput = truncateString(strings.ToValidUTF8(styledOutput, string(utf8.RuneError)), 10000)
_, err = t.tlp.PutMsg(ctx, database.TermlogTypeStdout, styledOutput, ...)
Defensive patterns

Strategy: fallback

Validate before calling

// pre-flight DB check in deployment health routine
if err := db.PingContext(ctx); err != nil {
    log.Warn("database unreachable; termlog writes will fail")
}

Try / catch

out, err := term.ExecCommand(ctx, flowID, cmd, false)
if err != nil && strings.Contains(err.Error(), "failed to put terminal log") {
    // command succeeded; only logging failed — treat output as usable with degraded audit
    log.Warn("termlog stdout write failed", "err", err)
}

Prevention

When it happens

Trigger: PutMsg(ctx, database.TermlogTypeStdout, styledOutput, ...) returns an error — typically the database write fails: DB connection lost, transaction/lock issues, or the storage layer rejects the (possibly large or invalid-UTF8) output payload.

Common situations: PostgreSQL outage or connection-pool exhaustion during heavy pentest activity; output exceeding a size limit in the termlog table; encoding issues in binary command output.

Related errors


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