Handle broken causal_conv1d at import time (#4047)

* Handle broken causal_conv1d import at runtime

Add a startup import-time probe for causal_conv1d and disable the fast path when the shared library is ABI broken. This keeps Falcon H1/model loading resilient without requiring env flags.

- Add disable_broken_causal_conv1d in import_fixes.
- Invoke it early from unsloth/__init__ during package init.
- Make Falcon H1 optional imports in loader and models/__init__ soft-fail instead of failing hard.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Enforce unavailable semantics for broken causal_conv1d

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove Falcon H1 import swallowing

* Restore optional Falcon H1 import guard

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove causal_conv1d regression tests

* Trim FA2 fallback messaging

---------

Co-authored-by: Daniel Hanchen <danielhanchen@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Daniel Han 2026-02-13 21:20:25 -08:00 committed by GitHub
commit 7254d47da1
3 changed files with 162 additions and 4 deletions

View file

@ -28,16 +28,19 @@ already_imported = [mod for mod in critical_modules if mod in sys.modules]
from .import_fixes import (
fix_message_factory_issue,
check_fbgemm_gpu_version,
disable_broken_causal_conv1d,
torchvision_compatibility_check,
fix_diffusers_warnings,
fix_huggingface_hub,
)
disable_broken_causal_conv1d()
fix_message_factory_issue()
check_fbgemm_gpu_version()
torchvision_compatibility_check()
fix_diffusers_warnings()
fix_huggingface_hub()
del disable_broken_causal_conv1d
del fix_message_factory_issue
del check_fbgemm_gpu_version
del torchvision_compatibility_check

View file

@ -13,6 +13,8 @@
# limitations under the License.
import os
import importlib.abc
import importlib.machinery
import importlib.util
from pathlib import Path
from importlib.metadata import version as importlib_version
@ -21,6 +23,7 @@ import re
import logging
import textwrap
import warnings
import sys
# We cannot do from unsloth_zoo.log import logger since FBGEMM might cause seg faults.
UNSLOTH_ENABLE_LOGGING = os.environ.get("UNSLOTH_ENABLE_LOGGING", "0") in (
@ -1163,3 +1166,157 @@ def disable_torchcodec_if_broken():
tf_import_utils._torchcodec_available = False
except (ImportError, AttributeError):
pass
CAUSAL_CONV1D_BROKEN = False
_CAUSAL_CONV1D_PREFIX = "causal_conv1d"
_CAUSAL_CONV1D_BLOCKER_SENTINEL = "_unsloth_causal_conv1d_blocker"
def _is_causal_conv1d_name(module_name: str) -> bool:
return module_name == _CAUSAL_CONV1D_PREFIX or module_name.startswith(
_CAUSAL_CONV1D_PREFIX + "."
)
def _resolve_module_name(module_name, package):
if not isinstance(module_name, str):
return module_name
if module_name.startswith("."):
try:
return importlib.util.resolve_name(module_name, package)
except Exception:
return module_name
return module_name
def _is_broken_causal_conv1d_error(error) -> bool:
checked = set()
current = error
while current is not None and id(current) not in checked:
checked.add(id(current))
message = str(current).lower()
if (
("causal_conv1d_cuda" in message and "undefined symbol" in message)
or ("_zn3c103hip28c10_hip_check_implementation" in message)
or ("causal_conv1d" in message and "undefined symbol" in message)
):
return True
current = getattr(current, "__cause__", None) or getattr(
current, "__context__", None
)
return False
class _CausalConv1dImportBlockerLoader(importlib.abc.Loader):
__slots__ = ("module_name",)
def __init__(self, module_name):
self.module_name = module_name
def create_module(self, spec):
return None
def exec_module(self, module):
raise ModuleNotFoundError(f"No module named '{self.module_name}'")
class _CausalConv1dImportBlockerFinder(importlib.abc.MetaPathFinder):
__slots__ = (_CAUSAL_CONV1D_BLOCKER_SENTINEL,)
def __init__(self):
setattr(self, _CAUSAL_CONV1D_BLOCKER_SENTINEL, True)
def find_spec(self, fullname, path = None, target = None):
if not CAUSAL_CONV1D_BROKEN or not _is_causal_conv1d_name(fullname):
return None
return importlib.machinery.ModuleSpec(
name = fullname,
loader = _CausalConv1dImportBlockerLoader(fullname),
is_package = fullname == _CAUSAL_CONV1D_PREFIX,
)
def _patch_find_spec_for_causal_conv1d():
current_find_spec = importlib.util.find_spec
if getattr(current_find_spec, "_unsloth_causal_conv1d_find_spec_patch", False):
return
def _blocked_find_spec(name, package = None):
resolved_name = _resolve_module_name(name, package)
if CAUSAL_CONV1D_BROKEN and isinstance(resolved_name, str):
if _is_causal_conv1d_name(resolved_name):
return None
return current_find_spec(name, package)
_blocked_find_spec._unsloth_causal_conv1d_find_spec_patch = True
_blocked_find_spec._unsloth_original_find_spec = current_find_spec
importlib.util.find_spec = _blocked_find_spec
def _install_causal_conv1d_blocker():
_patch_find_spec_for_causal_conv1d()
for finder in sys.meta_path:
if getattr(finder, _CAUSAL_CONV1D_BLOCKER_SENTINEL, False):
return
sys.meta_path.insert(0, _CausalConv1dImportBlockerFinder())
def _clear_causal_conv1d_modules():
for module_name in list(sys.modules):
if _is_causal_conv1d_name(module_name):
sys.modules.pop(module_name, None)
def _disable_transformers_causal_conv1d():
try:
import transformers.utils.import_utils as tf_import_utils
except Exception:
return
if hasattr(tf_import_utils, "is_causal_conv1d_available"):
tf_import_utils.is_causal_conv1d_available = lambda: False
for attr_name in (
"_causal_conv1d_available",
"_is_causal_conv1d_available",
):
if hasattr(tf_import_utils, attr_name):
setattr(tf_import_utils, attr_name, False)
def disable_broken_causal_conv1d():
"""Disable causal_conv1d dynamically when its shared library is ABI-broken.
This mirrors Unsloth's FlashAttention fallback behavior: if importing causal_conv1d
fails with a known binary symbol error, we disable it at startup so model imports do
not hard-fail.
"""
global CAUSAL_CONV1D_BROKEN
if CAUSAL_CONV1D_BROKEN:
_install_causal_conv1d_blocker()
_disable_transformers_causal_conv1d()
return
try:
if importlib.util.find_spec("causal_conv1d") is None:
return
except Exception:
return
try:
import causal_conv1d # noqa: F401
return
except Exception as error:
if not _is_broken_causal_conv1d_error(error):
return
CAUSAL_CONV1D_BROKEN = True
_clear_causal_conv1d_modules()
_install_causal_conv1d_blocker()
_disable_transformers_causal_conv1d()
print(
"Unsloth: Detected broken causal_conv1d binary; "
"disabling causal_conv1d fast path and continuing import."
)

View file

@ -845,8 +845,7 @@ if DEVICE_TYPE == "cuda":
"Unsloth: Your Flash Attention 2 installation seems to be broken?\n"
"A possible explanation is you have a new CUDA version which isn't\n"
"yet compatible with FA2? Please file a ticket to Unsloth or FA2.\n"
"We shall now use Xformers instead, which does not have any performance hits!\n"
"We found this negligible impact by benchmarking on 1x A100."
"We shall now use Xformers instead, which does not have any performance hits!"
)
# Stop Flash Attention from importing!
@ -897,8 +896,7 @@ elif DEVICE_TYPE == "hip":
"Unsloth: Your Flash Attention 2 installation seems to be broken?\n"
"A possible explanation is you have a new CUDA version which isn't\n"
"yet compatible with FA2? Please file a ticket to Unsloth or FA2.\n"
"We shall now use Xformers instead, which does not have any performance hits!\n"
"We found this negligible impact by benchmarking on 1x A100."
"We shall now use Xformers instead, which does not have any performance hits!"
)
# Stop Flash Attention from importing!