microsoft/semantic-kernel · error · AgentInitializationException

Unresolved placeholders in spec

Error message

Unresolved placeholders in spec: {', '.join(f'${{{key}}}' for key in unresolved)}

What it means

Raised by resolve_placeholders() after running regex substitution when one or more ${...} placeholders remain in the YAML string. The replacer only substitutes keys under the OpenAI: section that exist in field_mapping (ChatModelId, AgentId, ApiKey) or in extras; any placeholder whose key is unknown is left intact and then flagged here so the spec is never silently shipped with template holes.

Solutions

  1. Provide the missing value via extras={'SomeField': value} or set the corresponding env var so the field_mapping entry is non-empty.
  2. Correct placeholder key names to match ChatModelId/AgentId/ApiKey exactly.
  3. Remove placeholders the resolver cannot fill, or pre-substitute non-OpenAI sections yourself before calling resolve_placeholders.

Example fix

# before
resolve_placeholders(yaml_str, settings=openai_settings)
# yaml has ${OpenAI:ProjectId}

# after
resolve_placeholders(yaml_str, settings=openai_settings, extras={'ProjectId': 'proj_123'})
Defensive patterns

Strategy: validation

Validate before calling

import re
placeholders = re.findall(r'\$\{([^}]+)\}', yaml_str)
known = {'ChatModelId','AgentId','ApiKey'} | set(extras or {})
unresolved = [p for p in placeholders if p.split(':')[-1] not in known]
assert not unresolved, f'unresolved: {unresolved}'

Type guard

null

Try / catch

from semantic_kernel.exceptions.agent_exceptions import AgentInitializationException
try:
    out = resolve_placeholders(yaml_str, settings=s, extras=extras)
except AgentInitializationException as e:
    if 'Unresolved' in str(e):
        # parse missing keys from message, supply via extras, retry
        extras = extras or {}; extras.update(supply_missing())
        out = resolve_placeholders(yaml_str, settings=s, extras=extras)

Prevention

When it happens

Trigger: A spec contains ${OpenAI:SomeField} where SomeField is not chat_model_id/agent_id/api_key and not in extras; a non-OpenAI placeholder like ${Other:Key} that the replacer deliberately leaves untouched (section != OpenAI); typos in placeholder names; missing env values so field_mapping entries are None and the 'or' chain falls through to the original match.

Common situations: Templated YAML with placeholders for values not provided via env/extras; placeholder naming mismatch (ApiKey vs APIKey); settings loaded but a field is None so the value resolves falsy; referencing a section the resolver does not handle.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/c72bd7b0d08cafc1. Report an issue: GitHub.

Appendix: source

Thrown at python/semantic_kernel/agents/open_ai/openai_assistant_agent.py:594

        if extras:
            field_mapping.update(extras)

        def replacer(match: re.Match[str]) -> str:
            """Replace the matched placeholder with the corresponding value from field_mapping."""
            full_key = match.group(1)  # for example, OpenAI:ApiKey
            section, _, key = full_key.partition(":")
            if section != "OpenAI":
                return match.group(0)

            # Try short key first (ApiKey), then full (OpenAI:ApiKey)
            return str(field_mapping.get(key) or field_mapping.get(full_key) or match.group(0))

        result = pattern.sub(replacer, yaml_str)

        # Safety check for unresolved placeholders
        unresolved = pattern.findall(result)
        if unresolved:
            raise AgentInitializationException(
                f"Unresolved placeholders in spec: {', '.join(f'${{{key}}}' for key in unresolved)}"
            )

        return result

    # endregion

    # region Tool Handling

    @staticmethod
    def configure_code_interpreter_tool(
        file_ids: str | list[str] | None = None, **kwargs: Any
    ) -> tuple[list["AssistantToolParam"], ToolResources]:
        """Generate tool + tool_resources for the code_interpreter."""
        if isinstance(file_ids, str):
            file_ids = [file_ids]
        tool: "CodeInterpreterToolParam" = {"type": "code_interpreter"}
        resources: ToolResources = {}

View on GitHub (pinned to c028a0c7dc)