hiyouga/LlamaFactory · error · RuntimeError
Expected a string, got {value}
Error message
Expected a string, got {value} What it means
Inside StringFormatter.apply, for each string slot every kwarg value is substituted; a value that is not a Python str raises RuntimeError. apply receives fields extracted from the dataset example (query, response, system, content, ...), so a non-string column value — None (missing field), int/float, list, or dict — triggers this.
Source
Thrown at src/llamafactory/data/formatter.py:78
@dataclass
class StringFormatter(Formatter):
def __post_init__(self):
has_placeholder = False
for slot in filter(lambda s: isinstance(s, str), self.slots):
if re.search(r"\{\{[a-zA-Z_][a-zA-Z0-9_]*\}\}", slot):
has_placeholder = True
if not has_placeholder:
raise ValueError("A placeholder is required in the string formatter.")
@override
def apply(self, **kwargs) -> SLOTS:
elements = []
for slot in self.slots:
if isinstance(slot, str):
for name, value in kwargs.items():
if not isinstance(value, str):
raise RuntimeError(f"Expected a string, got {value}")
slot = slot.replace("{{" + name + "}}", value, 1)
elements.append(slot)
elif isinstance(slot, (dict, set)):
elements.append(slot)
else:
raise RuntimeError(f"Input must be string, set[str] or dict[str, str], got {type(slot)}.")
return elements
@dataclass
class FunctionFormatter(StringFormatter):
def __post_init__(self):
super().__post_init__()
self.tool_utils = get_tool_utils(self.tool_format)
@overrideView on GitHub (pinned to f28afaf635)
Solutions
- Inspect the failing example: the message prints the offending value, which identifies the missing/mistyped column.
- Fix dataset_info.json column mapping (e.g. output column name) so every placeholder resolves to a string field.
- Normalize the dataset: cast columns to str, fill missing fields with "", and ensure single-turn templates get scalar strings not lists.
- If a field can legitimately be absent, preprocess to empty string before training.
Example fix
# before: dataset rows have null "output" for some examples
# StringFormatter "{{query}} -> {{response}}" receives response=None
# after: clean the dataset first
ds = ds.map(lambda x: {"output": x["output"] or ""}) # and map output->response in dataset_info Defensive patterns
Strategy: validation
Validate before calling
# validate dataset fields against template placeholders before training
PLACEHOLDERS = {"query", "response", "system", "content", "tools"}
for ex in itertools.islice(dataset, 100):
for k in PLACEHOLDERS & set(ex):
assert ex[k] is None or isinstance(ex[k], str), f"column {k} must be str or None, got {type(ex[k])}" Type guard
def is_str_or_none(v) -> bool:
return v is None or isinstance(v, str) Try / catch
try:
encoded = template.encode(example)
except RuntimeError as e:
if "Expected a string" in str(e):
logger.error("non-string column in example %s — fix column mapping", example)
raise Prevention
- Map dataset column names correctly in dataset_info.json (e.g. output->response).
- Preprocess: fill nulls with "" and cast columns to string.
- Ensure single-turn templates receive scalar strings, not conversation lists.
When it happens
Trigger: Formatting a dataset whose examples lack a field used by the template (None substituted); columns stored as lists (e.g. multi-turn content arrays fed to a single-turn template); numeric labels in a column mapped to a placeholder.
Common situations: Dataset schema drift: field renamed (output -> response) so lookups return None; sharegpt conversations lists passed to alpaca formatting; JSON datasets where a value parses as number instead of string.
Related errors
- Stage does not supported: {stage}.
- Empty formatter should not contain any placeholder.
- A placeholder is required in the string formatter.
- The model does not have a submodule named '{submodule_name}'
- Unsupported model type: {getattr(config, 'model_type')}.
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/da081c3d9ec074d6.
Report an issue: GitHub.