{"record":{"id":"37f5736a649cb051","repo":"huggingface/transformers","slug":"skip-must-be-an-iterable-of-strings","errorCode":null,"errorMessage":"`skip` must be an iterable of strings.","messagePattern":"`skip` must be an iterable of strings\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/transformers/integrations/heterogeneity/configuration_utils.py","lineNumber":52,"sourceCode":"\nclass AmbiguousGlobalPerLayerAttributeError(RuntimeError):\n    \"\"\"Raised when a per-layer attribute is read from a heterogeneous global config.\"\"\"\n\n\n@dataclass\nclass _HeterogeneitySpec:\n    per_layer_overrides: dict[int, dict[str, Any]]\n    per_layer_attributes: set[str]\n    explicit_per_layer_attributes: set[str]\n\n\ndef _normalize_layer_overrides(layer_overrides: dict[str, Any]) -> dict[str, Any]:\n    normalized = copy.deepcopy(layer_overrides)\n\n    if \"skip\" in normalized:\n        skip = normalized.pop(\"skip\")\n        if isinstance(skip, str) or not isinstance(skip, Iterable):\n            raise TypeError(\"`skip` must be an iterable of strings.\")\n\n        skip = set(skip)\n        if not all(isinstance(item, str) for item in skip):\n            raise TypeError(\"`skip` must contain only strings.\")\n\n        if skip:\n            normalized[\"skip\"] = sorted(skip)\n\n    return normalized\n\n\ndef _validate_layer_indices(config: PreTrainedConfig, per_layer_overrides: dict[int, dict[str, Any]]) -> None:\n    if not per_layer_overrides:\n        return\n\n    num_hidden_layers = config.num_hidden_layers\n    invalid_layer_indices = [\n        layer_idx for layer_idx in per_layer_overrides if layer_idx < 0 or layer_idx >= num_hidden_layers","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/huggingface/transformers/blob/a597f974857b3d92939971296bc0deb93d33d780/src/transformers/integrations/heterogeneity/configuration_utils.py#L34-L70","documentation":"_normalize_layer_overrides validates the per-layer 'skip' entry used by heterogeneity (layer-wise heterogeneous) configs. A bare string is rejected (it would be interpreted as an iterable of characters) and non-iterables are rejected too: skip must be an iterable (list/set/tuple) whose elements are strings naming things to skip. Violations raise TypeError immediately at config normalization time.","triggerScenarios":"Setting per_layer_config = {i: {\"skip\": \"mlp\"}} or {\"skip\": 3} in a model config with heterogeneity enabled — the string/int form hits the isinstance guard and raises TypeError.","commonSituations":"Users writing a single layer name instead of a list; JSON configs where skip was simplified to a string during hand-editing; assuming skip counts layers rather than names modules.","solutions":["Wrap the value in a list: \"skip\": [\"mlp\"] instead of \"skip\": \"mlp\"","Ensure every element is a string naming a component to skip"],"exampleFix":"# before\nconfig.per_layer_config = {0: {\"skip\": \"mlp\"}}\n\n# after\nconfig.per_layer_config = {0: {\"skip\": [\"mlp\"]}}","handlingStrategy":"type-guard","validationCode":"def normalize_skip(overrides):\n    for layer, ov in overrides.items():\n        if \"skip\" in ov:\n            s = ov[\"skip\"]\n            assert not isinstance(s, str) and isinstance(s, Iterable), \"skip must be a list of strings\"\n            ov[\"skip\"] = [str(x) for x in s]\n    return overrides","typeGuard":"from typing import Iterable\n\ndef is_valid_skip(v) -> bool:\n    return (\n        not isinstance(v, str)\n        and isinstance(v, Iterable)\n        and all(isinstance(item, str) for item in v)\n    )","tryCatchPattern":null,"preventionTips":["Always write skip as a list in configs: \"skip\": [\"mlp\"]","Validate config dicts against a schema (pydantic/jsonschema) before assigning per_layer_config"],"tags":["heterogeneity","config","type-validation","per-layer"],"backgroundTag":null,"analyzedSha":"a597f974857b3d92939971296bc0deb93d33d780","analyzedAt":"2026-08-14T18:24:08.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}