{"record":{"id":"acf2c9a4cdd8af93","repo":"vxcontrol/pentagi","slug":"subtask-is-waiting-put-input-first","errorCode":null,"errorMessage":"subtask is waiting, put input first","messagePattern":"subtask is waiting, put input first","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/subtask.go","lineNumber":300,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to put input for subtask %d: %w\", stw.subtaskCtx.SubtaskID, err)\n\t}\n\n\tstw.mx.Lock()\n\tdefer stw.mx.Unlock()\n\n\tstw.waiting = false\n\n\treturn nil\n}\n\nfunc (stw *subtaskWorker) Run(ctx context.Context) error {\n\tif stw.IsCompleted() {\n\t\treturn fmt.Errorf(\"subtask has already completed\")\n\t}\n\n\tif stw.IsWaiting() {\n\t\treturn fmt.Errorf(\"subtask is waiting, put input first\")\n\t}\n\n\tif err := stw.SetStatus(ctx, database.SubtaskStatusRunning); err != nil {\n\t\tstw.handleInterrupting(err)\n\t\treturn err\n\t}\n\n\tvar (\n\t\ttaskID     = stw.subtaskCtx.TaskID\n\t\tsubtaskID  = stw.subtaskCtx.SubtaskID\n\t\tmsgChainID = stw.subtaskCtx.MsgChainID\n\t)\n\n\tif err := stw.subtaskCtx.Provider.EnsureChainConsistency(ctx, msgChainID); err != nil {\n\t\tstw.handleInterrupting(err)\n\t\treturn fmt.Errorf(\"failed to ensure chain consistency for subtask %d: %w\", subtaskID, err)\n\t}\n","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/subtask.go#L282-L318","documentation":"Run requires the subtask to NOT be waiting: if the previous chain execution ended with PerformResultWaiting, the agent is blocked on user input and Run must not be re-invoked until that input is supplied via PutInput (which flips waiting back to false). The error is a sequencing guard on the subtask state machine.","triggerScenarios":"Calling Run(ctx) while IsWaiting() is true — i.e. re-running a subtask that stopped to ask the user a question, before PutInput was called; also a first Run call racing with an early transition to Waiting.","commonSituations":"Retry loops that call Run on a fixed interval without checking IsWaiting; a UI 'resume' button that calls Run instead of first submitting the pending user answer.","solutions":["Call PutInput(ctx, answer) with the pending user input first; only after waiting=false, call Run to resume the chain.","Check IsWaiting() before Run and route to the 'await user input' path instead of failing.","Fix automation that polls Run; subscribe to subtask status changes and resume only after input was accepted."],"exampleFix":"// before\nif err := worker.Run(ctx); err != nil {\n\treturn err\n}\n// after\nif worker.IsWaiting() {\n\treturn fmt.Errorf(\"awaiting user input for subtask; call PutInput before Run\")\n}\nreturn worker.Run(ctx)","handlingStrategy":"validation","validationCode":"// route waiting subtasks to the input path, not Run\nif worker.IsWaiting() {\n\treturn fmt.Errorf(\"pending user input required\")\n}\nreturn worker.Run(ctx)","typeGuard":null,"tryCatchPattern":"if err := worker.Run(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"subtask is waiting, put input first\") {\n\t\t// notify the UI that a user answer is required\n\t\treturn ErrInputRequired\n\t}\n\treturn err\n}","preventionTips":["Model the lifecycle explicitly: Running → Waiting ⇒ require input before Run.","Never poll Run blindly; drive transitions from perform results.","UI resume buttons must submit the pending answer (PutInput), not call Run.","Log state transitions to debug sequencing bugs."],"tags":["go","state-machine","lifecycle","waiting-for-input"],"backgroundTag":"invalid-subtask-state-transition","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}