Skip to content

Suppress "Placeholders have not been resolved" warning when MessagesPlaceholder use is intentional (LangChain integration) #1667

Description

@s0ftnote

Summary

ChatPromptClient.compile() (and get_langchain_prompt() which calls it
under the hood) unconditionally logs:

Placeholders ['...'] have not been resolved. Pass them as keyword arguments to compile().

even when leaving placeholders unresolved is intentional — i.e. the
caller follows the officially documented LangChain integration pattern
where unresolved placeholders are deliberately returned as
MessagesPlaceholder for LangChain's ChatPromptTemplate to resolve at
.invoke() time.

Result: production logs are flooded with one WARNING per request, masking
real signals.

Reproduction (langfuse 4.6.1, official docs example)

from langfuse import get_client
from langchain_core.prompts import ChatPromptTemplate

langfuse = get_client()

# Create a chat prompt with a placeholder (per docs example)
langfuse.create_prompt(
    name="movie-critic-chat",
    type="chat",
    prompt=[
        {"role": "system", "content": "You are an expert movie critic"},
        {"type": "placeholder", "name": "chat_history"},
        {"role": "user", "content": "What should I watch next?"},
    ],
    labels=["production"],
)

# Documented LangChain integration:
# https://langfuse.com/docs/prompt-management/features/message-placeholders
prompt = langfuse.get_prompt("movie-critic-chat")
langchain_prompt = ChatPromptTemplate.from_template(prompt.get_langchain_prompt())

# → logs WARNING: Placeholders ['chat_history'] have not been resolved.
#   even though leaving it unresolved is EXACTLY what the docs tell you
#   to do for LangChain integration.

Why this is a problem

  1. The docs at /docs/prompt-management/features/message-placeholders
    (LangChain Python tab) endorse this exact pattern — MessagesPlaceholder
    objects in the output are the documented goal.
  2. SDK can't distinguish "intentional unresolved (LangChain hand-off)" from
    "misuse (forgot kwargs)" and warns in both cases.
  3. No public API to silence: no silent= / suppress_warnings= kwarg on
    compile() or get_langchain_prompt().
  4. logging.getLogger("langfuse").setLevel(...) is not a reliable
    workaround — see bug: changing langfuse log level in python doesn't affect the logger langfuse#4976 reporting setLevel doesn't
    affect certain SDK-emitted warnings.
  5. Forces every LangChain integrator to install a logging.Filter that
    string-matches the warning message — brittle (breaks on copy edits)
    and easy to over-silence.

Suggested fix

Add a silent=True (or suppress_unresolved_warning=True) kwarg to:

  • ChatPromptClient.compile(**kwargs, silent: bool = False)
  • ChatPromptClient.get_langchain_prompt(**kwargs, silent: bool = False)

When silent=True, the
"Placeholders ['...'] have not been resolved" warning is skipped, but
other validation warnings (e.g. "Placeholder X must contain a list of
chat messages"
) still fire — those are genuine misuse signals.

Alternatively, get_langchain_prompt() could pass silent=True to its
internal compile() call by default, since by definition any unresolved
placeholder returned from this method is intended for LangChain.

Version info

  • langfuse-python: 4.6.1 (also reproduces on 4.5.1 + main HEAD)
  • langchain-core: 1.3.2
  • Python: 3.11

Current workaround we use

contextlib.contextmanager that scopes a logging.Filter matching the
substring "have not been resolved" over the
chat_skeleton.get_langchain_prompt() call site. Fragile (depends on
warning string), but scoped narrowly so other langfuse warnings still
propagate.

Happy to send a PR if the maintainers agree with the kwarg shape.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions