From 9e74f450cc224aa966e122cbdbf4fa164b47f623 Mon Sep 17 00:00:00 2001 From: Roy Belio Date: Thu, 6 Nov 2025 15:36:18 +0200 Subject: [PATCH] fix: installer permission error on macOS This commit addresses issue #4005 where the one-line installer was failing on macOS with a PermissionError when attempting to create a directory named "None". Changes: 1. Fix external_providers_dir None handling (src/llama_stack/cli/stack/run.py) - Changed from truthy check to explicit 'is not None' check - Use Path objects directly instead of str() conversion to avoid converting None to the string "None" - Added proper error handling with descriptive messages for permission errors - Added logging when creating external providers directory 2. Improve type conversion documentation (src/llama_stack/core/stack.py) - Added comments explaining that empty strings from env var defaults are converted to None - Documented that code must check for None explicitly to avoid str(None) creating the literal string "None" Testing performed: - All unit tests pass (8/8 in test_stack_config.py) - All pre-commit hooks pass - Manual testing on Apple M4 (ARM64) with Podman - Validated that None values are handled properly without string conversion Fixes #4005 --- src/llama_stack/cli/stack/run.py | 13 +++++++++++-- src/llama_stack/core/stack.py | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/llama_stack/cli/stack/run.py b/src/llama_stack/cli/stack/run.py index 73d8d13d5..cd35b5659 100644 --- a/src/llama_stack/cli/stack/run.py +++ b/src/llama_stack/cli/stack/run.py @@ -176,8 +176,17 @@ class StackRun(Subcommand): try: config = parse_and_maybe_upgrade_config(config_dict) # Create external_providers_dir if it's specified and doesn't exist - if config.external_providers_dir and not os.path.exists(str(config.external_providers_dir)): - os.makedirs(str(config.external_providers_dir), exist_ok=True) + if config.external_providers_dir is not None: + ext_dir = Path(config.external_providers_dir) + if not ext_dir.exists(): + try: + logger.info(f"Creating external providers directory: {ext_dir}") + ext_dir.mkdir(parents=True, exist_ok=True) + except (OSError, PermissionError) as e: + self.parser.error( + f"Failed to create external_providers_dir '{ext_dir}': {e}\n" + f"Please ensure you have write permissions or specify a different path." + ) except AttributeError as e: self.parser.error(f"failed to parse config file '{config_file}':\n {e}") diff --git a/src/llama_stack/core/stack.py b/src/llama_stack/core/stack.py index 2ed0eccd2..c6ecf79b9 100644 --- a/src/llama_stack/core/stack.py +++ b/src/llama_stack/core/stack.py @@ -309,6 +309,9 @@ def _convert_string_to_proper_type(value: str) -> Any: # providers config should be typed this way. # TODO: we could try to load the config class and see if the config has a field with type 'str | None' # and then convert the empty string to None or not + # NOTE: Empty strings from env var defaults (e.g., ${VAR:=}) are converted to None. + # Code that uses these values MUST check for None explicitly (not just truthiness) + # to avoid converting None to the string "None" later. if value == "": return None