Skip to content

Conversation

@ssam18
Copy link

@ssam18 ssam18 commented Dec 2, 2025

Problem

Fixes #34156

When users provide a custom state_schema to create_agent() with additional fields that have default values (e.g., requirements_gathered: bool = False), those fields raise a KeyError when accessed in middleware. This happens because TypedDict class attributes with default values are only type hints - they don't automatically populate runtime dictionaries.

Before

class GraphState(AgentState):
    requirements_gathered: bool = False

@dynamic_prompt
def change_system_prompt(request: ModelRequest):
    if request.state["requirements_gathered"]:  # KeyError!
        return SEARCH_HOTELS_INSTRUCTION
    else:
        return REQUIREMENT_GATHERING_INSTRUCTION

agent = create_agent(
    model=llm,
    state_schema=GraphState,
    middleware=[change_system_prompt]
)

Error

KeyError: 'requirements_gathered'

Solution

Extract default values from the state schema class and merge them with the runtime state before passing to middleware. This ensures custom fields are always accessible, even when not explicitly provided in the input.

Changes

  1. Added _get_state_defaults() helper function - Extracts default values from state schema class by inspecting class attributes across the inheritance chain
  2. Modified model_node() and amodel_node() - Merge state defaults with runtime state before creating ModelRequest
  3. Maintains backward compatibility - Explicitly provided values in input override defaults

After

# Same code now works without KeyError
agent.invoke({"messages": [HumanMessage("test")]})
# Middleware can access request.state["requirements_gathered"] = False

Testing

Added comprehensive test coverage in test_custom_state_schema.py:

  • ✅ Test that custom fields with defaults are accessible in middleware
  • ✅ Test that explicitly provided values override defaults
  • ✅ Test that direct dictionary access doesn't raise KeyError

Backward Compatibility

  • No breaking changes
  • Existing code continues to work unchanged
  • Only affects behavior when custom state_schema is provided with default values
  • Runtime values always take precedence over defaults

When users provide a custom state_schema to create_agent with additional
fields that have default values (e.g., requirements_gathered: bool = False),
those fields were not accessible in middleware because TypedDict defaults
are type hints only - they don't automatically populate runtime dicts.

Changes:
1. Add _get_state_defaults() helper to extract default values from state schema
2. Merge state defaults with runtime state in model_node and amodel_node
3. Ensure middleware can access custom fields even when not in input
4. Add comprehensive tests for custom state schemas with defaults

The fix ensures that middleware can safely access custom state fields using
direct dictionary access (request.state['custom_field']) without KeyError,
while still allowing users to override defaults by providing values in input.

Fixes langchain-ai#34156
@github-actions github-actions bot added the langchain Related to the package `langchain` label Dec 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

langchain Related to the package `langchain`

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom state schema passed to create_agent doesnt come within middleware

1 participant