shareAI-lab/learn-claude-code · error · WorkflowInputError

workflow '{meta['name']}' denied by settings

Error message

workflow '{meta['name']}' denied by settings

What it means

check_permission applies the s03 allow/deny gate before launch: if meta['name'] appears in settings['deny'], the workflow is refused with WorkflowInputError naming the denied workflow. This is a policy enforcement error, not a data-format one — the meta is valid but the operator has banned this workflow by name.

Source

Thrown at s16_workflow_runtime/code.py:144

    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."""
    settings = settings or {}
    if meta["name"] in settings.get("deny", []):
        raise WorkflowInputError(f"workflow '{meta['name']}' denied by settings")
    return "allow"


# -- Minimal JSON Schema --
class SimpleJsonSchema:
    """Tiny validator backing agent({schema}):
    object/array/string/boolean/number + required keys."""

    def __init__(self, schema):
        self.schema = schema

    def validate(self, value, schema=None):
        schema = self.schema if schema is None else schema
        if "enum" in schema and value not in schema["enum"]:
            return False, f"expected one of {schema['enum']}"
        t = schema.get("type")
        if t == "object":
            if not isinstance(value, dict):

View on GitHub (pinned to 985456f4ad)

Solutions

  1. If the workflow should run, remove or correct its name in the settings deny list
  2. If denial is intended, stop the invoking scheduler/job as well
  3. Pre-check before launch: if meta['name'] in settings.get('deny', []), skip gracefully instead of letting the exception propagate

Example fix

# before
settings = {"deny": ["ingest"]}
run(workflow, meta=meta, settings=settings)  # raises if name == 'ingest'

# after
if meta["name"] not in settings.get("deny", []):
    run(workflow, meta=meta, settings=settings)
Defensive patterns

Strategy: validation

Validate before calling

def workflow_is_allowed(meta: dict, settings: dict) -> bool:
    return meta.get("name") not in (settings or {}).get("deny", [])

assert workflow_is_allowed(meta, settings), f"{meta['name']!r} is denied by settings"

Type guard

def is_not_denied(meta: dict, settings: dict | None) -> bool:
    return meta.get("name") not in (settings or {}).get("deny", [])

Try / catch

try:
    check_permission(meta, settings)
except WorkflowInputError as exc:
    if "denied by settings" in str(exc):
        # intentional policy: skip gracefully instead of crashing the scheduler
        log.info("skipping denied workflow %s", meta["name"])
        return
    raise

Prevention

When it happens

Trigger: settings = {"deny": ["ingest"]} and run(meta={"name": "ingest", ...}). Deny lists copied from another environment where the same name means a different workflow. Names added to deny for incident response while scheduled jobs keep firing.

Common situations: Operators disabling a misbehaving workflow via settings, then a cron/scheduler still attempting it. Config promotion carrying a production deny list into staging where the name collides with a legitimate workflow.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/db74c271b7fc4d88. Report an issue: GitHub.