vxcontrol/pentagi · error

failed to parse %s args: %w

Error message

failed to parse %s args: %w

What it means

The wait_flow_completion tool failed to JSON-unmarshal the raw arguments it received into WaitFlowCompletionAction. The underlying decode error is wrapped with %w, so errors.Is/As still work against the original json.UnmarshalTypeError or SyntaxError. This is a defensive check at the boundary of the agent tool-call protocol: the LLM produced arguments that are not valid JSON or do not match the action struct.

Source

Thrown at backend/pkg/tools/flow_manager.go:545

// currently running task finishes or the caller-supplied timeout expires.
type waitFlowCompletionTool struct {
	flowID  int64
	db      database.Querier
	handler func(ctx context.Context) error
}

func NewWaitFlowCompletionTool(
	flowID int64,
	db database.Querier,
	handler func(ctx context.Context) error,
) *waitFlowCompletionTool {
	return &waitFlowCompletionTool{flowID: flowID, db: db, handler: handler}
}

func (t *waitFlowCompletionTool) Handle(ctx context.Context, name string, args json.RawMessage) (string, error) {
	var action WaitFlowCompletionAction
	if err := json.Unmarshal(args, &action); err != nil {
		return "", fmt.Errorf("failed to parse %s args: %w", WaitFlowCompletionToolName, err)
	}

	timeout := time.Duration(action.Timeout.Int64()) * time.Second
	switch {
	case timeout <= 0:
		timeout = waitFlowDefaultTimeout
	case timeout > waitFlowMaxTimeout:
		timeout = waitFlowMaxTimeout
	}

	tasks, err := t.db.GetFlowTasks(ctx, t.flowID)
	if err != nil {
		return "", fmt.Errorf("failed to check flow status: %w", err)
	}

	if len(tasks) == 0 {
		return fmt.Sprintf(
			"The automation has not been created yet — no tasks exist. "+

View on GitHub (pinned to ea665308ba)

Solutions

  1. Inspect the wrapped inner error to see if it is a SyntaxError (malformed JSON) or UnmarshalTypeError (wrong field type) and fix the args accordingly.
  2. Ensure the caller passes a valid JSON object with correct field types, e.g. {"timeout": 30} with timeout as a number.
  3. Validate/generate args through a typed struct or JSON-schema-aware client instead of hand-built strings.
  4. Re-send the tool call; LLM-generated JSON errors are often transient generation mistakes.

Example fix

// before (invalid: timeout as string)
args := `{"timeout": "30"}`
// after
args := `{"timeout": 30}`
Defensive patterns

Strategy: validation

Validate before calling

const args = { timeout: 30 };
const json = JSON.stringify(args); // throws on cycles, produces valid JSON
JSON.parse(json); // pre-validate round-trip

Type guard

function isWaitFlowCompletionAction(v: unknown): v is { timeout?: number } {
  return typeof v === 'object' && v !== null &&
    (!('timeout' in v) || typeof (v as any).timeout === 'number');
}

Try / catch

try {
  const out = await tool.call('wait_flow_completion', args);
} catch (err) {
  if (String(err).includes('failed to parse')) {
    // regenerate/repair args JSON and retry once
  }
}

Prevention

When it happens

Trigger: Calling wait_flow_completion with args that are malformed JSON (truncated string, unquoted keys, trailing commas), a non-object payload (e.g. a bare string or array), or a wrong-typed field (e.g. timeout: "30" as a string instead of a number, or nested unknown structure causing UnmarshalTypeError).

Common situations: An LLM generates slightly invalid JSON when constructing tool calls; a client SDK serializes the args object as a string instead of an object; prompt templating mangles the JSON; long outputs truncate mid-JSON when the caller builds args manually.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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