{"record":{"id":"bf3bc1b2be226b5d","repo":"vxcontrol/pentagi","slug":"container-not-found","errorCode":null,"errorMessage":"container not found","messagePattern":"container not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/termlog.go","lineNumber":63,"sourceCode":"\tmsg string,\n\tcontainerID int64,\n\ttaskID, subtaskID *int64,\n) (int64, error) {\n\ttlw.mx.Lock()\n\tdefer tlw.mx.Unlock()\n\n\tif _, ok := tlw.containers[containerID]; !ok {\n\t\t// try to update the container map\n\t\tcontainers, err := tlw.GetContainers(ctx)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\ttlw.containers = make(map[int64]struct{})\n\t\tfor _, container := range containers {\n\t\t\ttlw.containers[container.ID] = struct{}{}\n\t\t}\n\t\tif _, ok := tlw.containers[containerID]; !ok {\n\t\t\treturn 0, fmt.Errorf(\"container not found\")\n\t\t}\n\t}\n\n\ttermLog, err := tlw.db.CreateTermLog(ctx, database.CreateTermLogParams{\n\t\tType:        msgType,\n\t\tText:        database.SanitizeUTF8(msg),\n\t\tContainerID: containerID,\n\t\tFlowID:      tlw.flowID,\n\t\tTaskID:      database.Int64ToNullInt64(taskID),\n\t\tSubtaskID:   database.Int64ToNullInt64(subtaskID),\n\t})\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to create termlog: %w\", err)\n\t}\n\n\ttlw.pub.TerminalLogAdded(ctx, termLog)\n\n\treturn termLog.ID, nil","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/termlog.go#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"Exec sessions outliving their container's cleanup; passing container IDs captured before flow restart; race between container garbage collection and remaining buffered terminal lines.","solutions":["Verify the containerID belongs to this flow (check the containers table by flow_id)","Refresh/verify via GetContainers before writing; if the container is gone, drop or re-attach the terminal session","Return 404/bad-request to callers that pass arbitrary container IDs","Ensure container lifecycle code does not delete DB rows while exec output is still streaming","For new containers, wait until the container row is committed before writing terminal logs"],"exampleFix":"// before\nid, err := termWorker.PutMsg(ctx, db.TermlogTypeOutput, line, removedContainerID, nil, nil)\n// after\ncontainers, _ := termWorker.GetContainers(ctx)\nif !containsContainer(containers, removedContainerID) {\n    log.Warn().Int64(\"container_id\", removedContainerID).Msg(\"dropping output for removed container\")\n    return 0, nil\n}\nid, err := termWorker.PutMsg(ctx, db.TermlogTypeOutput, line, removedContainerID, nil, nil)","handlingStrategy":"validation","validationCode":"func hasContainer(ctx context.Context, w controller.FlowTermLogWorker, containerID int64) bool {\n    cs, err := w.GetContainers(ctx)\n    if err != nil { return false }\n    for _, c := range cs { if c.ID == containerID { return true } }\n    return false\n}","typeGuard":"func isContainerNotFound(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"container not found\")\n}","tryCatchPattern":"id, err := w.PutMsg(ctx, msgType, line, containerID, taskID, subtaskID)\nif isContainerNotFound(err) {\n    log.Warn().Int64(\"container_id\", containerID).Msg(\"dropping terminal output for unknown container\")\n    return 0, nil // or re-attach session to a valid container\n}\nif err != nil { return 0, err }","preventionTips":["Only pass container IDs obtained from GetContainers for this flow","Stop terminal sessions before container removal/cleanup runs","Validate containerID > 0 and comes from the flow's own exec session registry","Re-check container liveness after long idle periods before writing","Map this error to 404/bad-request at the API boundary"],"tags":["container","not-found","terminal"],"backgroundTag":"resource-not-found","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}