shareAI-lab/learn-claude-code · error · WorkflowInputError
meta must be an object literal
Error message
meta must be an object literal
What it means
validate_meta requires the workflow metadata argument to be a Python dict (object literal) before launch. Anything else — a JSON string, a list, None — raises WorkflowInputError immediately, because every subsequent check (name, description, phases) indexes into it.
Source
Thrown at s16_workflow_runtime/code.py:123
) from exc
yield
finally:
if handle is not None:
try:
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
finally:
handle.close()
local_lock.release()
with _run_locks_guard:
if not local_lock.locked() and _run_locks.get(run_id) is local_lock:
_run_locks.pop(run_id, None)
# -- Metadata Validation --
def validate_meta(meta):
"""Validate name, description, and optional phases before launch."""
if not isinstance(meta, dict):
raise WorkflowInputError("meta must be an object literal")
if not meta.get("name") or not meta.get("description"):
raise WorkflowInputError("meta requires `name` and `description`")
if not isinstance(meta["name"], str) or not WORKFLOW_NAME_RE.fullmatch(meta["name"]):
raise WorkflowInputError(
"meta.name must be a 1-64 character slug using letters, numbers, '.', '_', or '-'"
)
if not isinstance(meta["description"], str):
raise WorkflowInputError("meta.description must be a string")
if "phases" in meta:
if not isinstance(meta["phases"], list) or not all(
isinstance(phase, str) and phase for phase in meta["phases"]
):
raise WorkflowInputError("meta.phases must be a list of non-empty strings")
return meta
def check_permission(meta, settings=None):
"""Apply the s03 allow/deny gate before launching a workflow."""View on GitHub (pinned to 985456f4ad)
Solutions
- Parse before passing: json.loads(meta) / yaml.safe_load(text) and pass the resulting dict
- If using a dataclass, convert with dataclasses.asdict() at the call site
- Add a boundary check asserting isinstance(meta, dict) right after deserialization
Example fix
# before
meta = Path("workflow.json").read_text() # str
run(meta=meta, ...)
# after
meta = json.loads(Path("workflow.json").read_text())
run(meta=meta, ...) Defensive patterns
Strategy: type-guard
Validate before calling
def is_meta_object(meta) -> bool:
return isinstance(meta, dict)
if isinstance(meta, str):
meta = json.loads(meta) # or yaml.safe_load for YAML
assert is_meta_object(meta) Type guard
def is_workflow_meta(meta) -> bool:
"""Narrow to a dict suitable for validate_meta."""
return isinstance(meta, dict) Try / catch
try:
validate_meta(meta)
except WorkflowInputError as exc:
if "object literal" in str(exc):
meta = json.loads(meta) if isinstance(meta, str) else dict(meta)
validate_meta(meta)
else:
raise Prevention
- Parse config files at the boundary and pass dicts only
- Convert dataclasses with dataclasses.asdict() before run()
- Assert isinstance(meta, dict) right after deserialization
When it happens
Trigger: Calling run(json.dumps(meta), ...) instead of run(meta, ...). Loading meta from a YAML/JSON file and forgetting json.loads/yaml.safe_load. Passing a dataclass or namedtuple that quacks like meta but is not a dict.
Common situations: Config read from disk stays a string. APIs that forward a request body as text. Refactors that introduce a Meta dataclass without converting it at the boundary.
Related errors
- meta.description must be a string
- meta.phases must be a list of non-empty strings
- meta requires `name` and `description`
- meta.name must be a 1-64 character slug using letters, numbe
- invalid workflow runId
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/0fcf0342d30c9e8e.
Report an issue: GitHub.