Skip to content

Regression of #103: the failed to parse ... resume_input as JSON HITL warning leaks again — upstream inserted "legacy" into the message so the CLI's substring filter (_DropResumeJSONWarning) no longer matches #137

Description

@dkedar7

Summary

The exact symptom that closed issue #103 fixed is back on a clean pip install langstage-cli (0.6.30). On the headline HITL path, every time a user answers a plain-string interrupt() with free text, an internal ag_ui_langgraph WARNING leaks straight into the conversation, right above the correct reply:

failed to parse legacy resume_input as JSON, treating as string (thread_id='…', run_id='…', error=Expecting value: line 1 column 1 (char 0)): 'yes go ahead'

Root cause: #103 was fixed by installing a surgical logging filter (_DropResumeJSONWarning, cli.py:152) that drops any record whose message contains the substring "failed to parse resume_input as JSON" (_AGUI_RESUME_JSON_WARNING, cli.py:142). But the bundled ag-ui-langgraph has since bumped 0.0.42 → 0.0.43 and reworded the message — it now reads failed to parse legacy resume_input as JSON, … (the word legacy was inserted after parse). The filter substring is no longer a substring of the emitted message, so the record passes the filter and reaches the console again. The #103 fix silently no longer does anything.

This is a brittle-string-match regression: the CLI pins ag-ui-langgraph>=0.0.41 (no upper bound) and langstage-core[agui], and the warning text drifted underneath the hard-coded matcher.

Severity: minor (cosmetic noise; the resumed value is correct) — same impact class as #103/#126 — but it is a regression of a shipped, closed fix on the primary HITL feature, and it will keep silently re-breaking whenever the upstream wording moves.

Distinct from the still-open #126, which is about a different line on the same resume (forwardedProps.command.resume is deprecated …). This issue is specifically the JSON-parse WARNING that #103 was closed to suppress.

Environment

Minimal repro

hitl.py — a canonical plain-string interrupt (no API key, no model, no own checkpointer):

# hitl.py
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import MessagesState
from langgraph.types import interrupt
from langchain_core.messages import AIMessage

def ask(state):
    answer = interrupt({"question": "Approve?"})
    return {"messages": [AIMessage(content=f"Decision was: {answer!r}")]}

g = StateGraph(MessagesState)
g.add_node("ask", ask); g.add_edge(START, "ask"); g.add_edge("ask", END)
graph = g.compile()

Deterministic, non-interactive repro (the --no-interactive empty auto-resume is a non-JSON '', which always trips the same warning):

langstage-cli -a hitl.py:graph --no-interactive "go" 2>&1 >/dev/null

Prints (to stderr):

Auto-resuming generic interrupt with an empty value (--no-interactive)
forwardedProps.command.resume is deprecated; please send RunAgentInput.resume[] (thread_id='…', run_id='…')
failed to parse legacy resume_input as JSON, treating as string (thread_id='…', run_id='…', error=Expecting value: line 1 column 1 (char 0)): ''

Interactive repro (the user-visible one — same as #103): langstage-cli -a hitl.py:graph, type do it, select Provide a response, type yes go ahead. Literal output (ANSI stripped):

⠋ Thinking... 0sforwardedProps.command.resume is deprecated; please send RunAgentInput.resume[] (thread_id='ec4998db-…', run_id='4e789e94-…')
failed to parse legacy resume_input as JSON, treating as string (thread_id='ec4998db-…', run_id='4e789e94-…', error=Expecting value: line 1 column 1 (char 0)): 'yes go ahead'
⏺ Decision was: 'yes go ahead'

Proof the installed filter no longer matches

import logging
from langstage_cli.cli import _DropResumeJSONWarning, _AGUI_RESUME_JSON_WARNING
flt = _DropResumeJSONWarning()

# The message ag_ui_langgraph 0.0.43 now emits:
new = logging.LogRecord("ag_ui_langgraph.agent", logging.WARNING, "", 0,
    "failed to parse legacy resume_input as JSON, treating as string "
    "(thread_id=%r, run_id=%r, error=%s): %r",
    ("tid","rid","Expecting value: line 1 column 1 (char 0)",""), None)

# The message the filter was written for (0.0.42):
old = logging.LogRecord("ag_ui_langgraph.agent", logging.WARNING, "", 0,
    "failed to parse resume_input as JSON, treating as string", None, None)

print(_AGUI_RESUME_JSON_WARNING)   # 'failed to parse resume_input as JSON'
print(flt.filter(new))             # True  -> ALLOWED THROUGH (leaks)
print(flt.filter(old))             # False -> suppressed (the case #103 handled)

Output on 0.6.30:

failed to parse resume_input as JSON
True
False

The upstream source now reads (ag_ui_langgraph/agent.py:660):

logger.warning(
    "failed to parse legacy resume_input as JSON, treating as string "
    "(thread_id=%r, run_id=%r, error=%s): %r", ...)

Expected vs actual

Suggested fix


Filed by the nightly dogfood routine. Confirmed reproducible on a clean pip install langstage-cli (0.6.30, ag-ui-langgraph 0.0.43).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingroutineFiled by the daily power-user dogfooding routine

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions