{"record":{"id":"a7c52492e97ec8f8","repo":"vxcontrol/pentagi","slug":"submit-timed-out-after-s-the-flow-may-not-have","errorCode":null,"errorMessage":"submit timed out after %s — the flow may not have received the input. Call %s to check the current state before retrying","messagePattern":"submit timed out after (.+?) — the flow may not have received the input\\. Call (.+?) to check the current state before retrying","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/pkg/tools/flow_manager.go","lineNumber":739,"sourceCode":"\n\t// Determine mode for better response message\n\twaitingForAsk := false\n\tsubtasks, err := t.db.GetFlowSubtasks(ctx, t.flowID)\n\tif err == nil {\n\t\tfor _, st := range subtasks {\n\t\t\tif st.Status == database.SubtaskStatusWaiting {\n\t\t\t\twaitingForAsk = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tinputCtx, inputCancel := context.WithTimeout(ctx, flowOperationTimeout)\n\tdefer inputCancel()\n\n\tif err := t.handler(inputCtx, action.Input); err != nil {\n\t\tif errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n\t\t\treturn \"\", fmt.Errorf(\n\t\t\t\t\"submit timed out after %s — the flow may not have received the input. \"+\n\t\t\t\t\t\"Call %s to check the current state before retrying\",\n\t\t\t\tflowOperationTimeout, GetFlowStatusToolName)\n\t\t}\n\t\t// Flow is running (returned by sendAssistantFlowInput when status != waiting).\n\t\t// This is a transient condition — return a soft result so the LLM knows to call\n\t\t// stop_flow first instead of retrying indefinitely and crashing the chain.\n\t\tif strings.Contains(err.Error(), \"not in 'waiting' state\") ||\n\t\t\tstrings.Contains(err.Error(), \"cannot submit input\") {\n\t\t\treturn fmt.Sprintf(\n\t\t\t\t\"Cannot submit input: the flow automation is currently active (not in 'waiting' state). \"+\n\t\t\t\t\t\"Call %s first to stop the current execution, wait for confirmation, \"+\n\t\t\t\t\t\"then retry %s. Use %s to confirm the flow is 'waiting' before retrying\",\n\t\t\t\tStopFlowToolName, SubmitFlowInputToolName, GetFlowStatusToolName,\n\t\t\t), nil\n\t\t}\n\t\t// A missing message chain means the waiting subtask's execution context was lost\n\t\t// (most likely after a system restart or database cleanup while the subtask was at an ask checkpoint).","sourceCodeStart":721,"sourceCodeEnd":757,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/flow_manager.go#L721-L757","documentation":"The flow input handler (sendAssistantFlowInput) did not return within flowOperationTimeout, or its context was cancelled, so the tool reports that the input may not have been delivered. Because delivery is uncertain, the tool refuses to claim success and directs the caller to check flow state before retrying. This avoids double-submitting input on an ambiguous timeout.","triggerScenarios":"Calling submit_flow_input while the flow automation channel is slow or the assistant goroutine is blocked, so t.handler(inputCtx, input) exceeds flowOperationTimeout or the parent ctx is cancelled mid-submit.","commonSituations":"LLM provider latency or the flow worker being busy when the input is sent; submitting input concurrently with a running task; very short flowOperationTimeout configuration; operator cancelling the agent run mid-submit.","solutions":["Call get_flow_status to see whether the input was actually received before doing anything else.","If the flow is still in 'waiting' state, re-call submit_flow_input with the same input.","If the flow is running, the input likely arrived — do not resubmit; wait or call stop_flow if needed.","Increase flowOperationTimeout if slow LLM/worker responses routinely cause false timeouts."],"exampleFix":"// before: blindly retrying on timeout\nif _, err := tool.Handle(ctx, \"submit_flow_input\", args); err != nil {\n    tool.Handle(ctx, \"submit_flow_input\", args) // risk of double-delivery\n}\n// after: check state first, retry only if still waiting\nif _, err := tool.Handle(ctx, \"submit_flow_input\", args); err != nil {\n    status := getStatus(ctx) // get_flow_status\n    if status == \"waiting\" {\n        tool.Handle(ctx, \"submit_flow_input\", args)\n    }\n}","handlingStrategy":"validation","validationCode":"// Only resubmit when the flow is verifiably still waiting\nstatus := getFlowStatus(ctx)\nif status != \"waiting\" {\n    return // input likely delivered or flow busy — do not resend\n}","typeGuard":null,"tryCatchPattern":"_, err := tool.Handle(ctx, \"submit_flow_input\", args)\nif err != nil && strings.Contains(err.Error(), \"submit timed out\") {\n    // ambiguous outcome: check state before any retry\n    if getFlowStatus(ctx) == \"waiting\" {\n        _, _ = tool.Handle(ctx, \"submit_flow_input\", args)\n    }\n}","preventionTips":["Never blindly retry an input submit after a timeout — always check flow state first.","Tune flowOperationTimeout to exceed worst-case LLM/worker latency.","Avoid submitting input concurrently with a running task.","Log flow state transitions to diagnose ambiguous deliveries."],"tags":["timeout","flow-control","ambiguous-state"],"backgroundTag":"operation-timed-out","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}