vxcontrol/pentagi · error

container not found

Error message

container not found

What it means

PutMsg writes terminal output for a container, but first validates containerID against the worker's in-memory set, refreshing it from GetFlowContainers on miss. This error means the given containerID is not among the flow's containers in the database — i.e. the terminal output references a container that does not exist or belongs to another flow. Note that a GetContainers DB failure is returned raw (error 208) instead.

Source

Thrown at backend/pkg/controller/termlog.go:63

	msg string,
	containerID int64,
	taskID, subtaskID *int64,
) (int64, error) {
	tlw.mx.Lock()
	defer tlw.mx.Unlock()

	if _, ok := tlw.containers[containerID]; !ok {
		// try to update the container map
		containers, err := tlw.GetContainers(ctx)
		if err != nil {
			return 0, err
		}
		tlw.containers = make(map[int64]struct{})
		for _, container := range containers {
			tlw.containers[container.ID] = struct{}{}
		}
		if _, ok := tlw.containers[containerID]; !ok {
			return 0, fmt.Errorf("container not found")
		}
	}

	termLog, err := tlw.db.CreateTermLog(ctx, database.CreateTermLogParams{
		Type:        msgType,
		Text:        database.SanitizeUTF8(msg),
		ContainerID: containerID,
		FlowID:      tlw.flowID,
		TaskID:      database.Int64ToNullInt64(taskID),
		SubtaskID:   database.Int64ToNullInt64(subtaskID),
	})
	if err != nil {
		return 0, fmt.Errorf("failed to create termlog: %w", err)
	}

	tlw.pub.TerminalLogAdded(ctx, termLog)

	return termLog.ID, nil

View on GitHub (pinned to ea665308ba)

Solutions

  1. Verify the containerID belongs to this flow (check the containers table by flow_id)
  2. Refresh/verify via GetContainers before writing; if the container is gone, drop or re-attach the terminal session
  3. Return 404/bad-request to callers that pass arbitrary container IDs
  4. Ensure container lifecycle code does not delete DB rows while exec output is still streaming
  5. For new containers, wait until the container row is committed before writing terminal logs

Example fix

// before
id, err := termWorker.PutMsg(ctx, db.TermlogTypeOutput, line, removedContainerID, nil, nil)
// after
containers, _ := termWorker.GetContainers(ctx)
if !containsContainer(containers, removedContainerID) {
    log.Warn().Int64("container_id", removedContainerID).Msg("dropping output for removed container")
    return 0, nil
}
id, err := termWorker.PutMsg(ctx, db.TermlogTypeOutput, line, removedContainerID, nil, nil)
Defensive patterns

Strategy: validation

Validate before calling

func hasContainer(ctx context.Context, w controller.FlowTermLogWorker, containerID int64) bool {
    cs, err := w.GetContainers(ctx)
    if err != nil { return false }
    for _, c := range cs { if c.ID == containerID { return true } }
    return false
}

Type guard

func isContainerNotFound(err error) bool {
    return err != nil && strings.Contains(err.Error(), "container not found")
}

Try / catch

id, err := w.PutMsg(ctx, msgType, line, containerID, taskID, subtaskID)
if isContainerNotFound(err) {
    log.Warn().Int64("container_id", containerID).Msg("dropping terminal output for unknown container")
    return 0, nil // or re-attach session to a valid container
}
if err != nil { return 0, err }

Prevention

When it happens

Trigger: Calling PutMsg with a container ID from another flow, a container removed from Docker and deleted from the DB before output flushes, or a zero/uninitialized container ID.

Common situations: Exec sessions outliving their container's cleanup; passing container IDs captured before flow restart; race between container garbage collection and remaining buffered terminal lines.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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