{"record":{"id":"3889544821b931bf","repo":"warpdotdev/warp","slug":"config-unavailable","errorCode":"config_unavailable","errorMessage":"config_unavailable","messagePattern":"config_unavailable","errorType":"exception","errorClass":"MergeError","httpStatus":null,"severity":"error","filePath":"resources/bundled/skills/tui-migrate-setup/scripts/merge_mcp_config.py","lineNumber":111,"sourceCode":"            child = {}\n            current[segment] = child\n        current = child\n    if path:\n        current[path[-1]] = value\n    else:\n        document.clear()\n        document.update(value)\n\n\ndef _read_regular_file(path: Path, *, missing_ok: bool) -> tuple[bytes, int | None]:\n    try:\n        metadata = path.lstat()\n    except FileNotFoundError:\n        if missing_ok:\n            return b\"\", None\n        raise MergeError(\"source_config_unavailable\")\n    except OSError as error:\n        raise MergeError(\"config_unavailable\") from error\n\n    if stat.S_ISLNK(metadata.st_mode):\n        raise MergeError(\"config_symlink_rejected\")\n    if not stat.S_ISREG(metadata.st_mode):\n        raise MergeError(\"config_not_regular_file\")\n    if metadata.st_size > MAX_CONFIG_BYTES:\n        raise MergeError(\"config_too_large\")\n\n    flags = os.O_RDONLY\n    if hasattr(os, \"O_NOFOLLOW\"):\n        flags |= os.O_NOFOLLOW\n    try:\n        descriptor = os.open(path, flags)\n        with os.fdopen(descriptor, \"rb\") as file:\n            raw = file.read(MAX_CONFIG_BYTES + 1)\n    except OSError as error:\n        raise MergeError(\"config_unavailable\") from error\n    if len(raw) > MAX_CONFIG_BYTES:","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/resources/bundled/skills/tui-migrate-setup/scripts/merge_mcp_config.py#L93-L129","documentation":"config_unavailable from the lstat except-OSError branch: the metadata call failed with something other than FileNotFoundError — classically EACCES (search-permission denied on a parent directory), ELOOP (a loop of symlinks; a plain symlink is caught earlier by the S_ISLNK check, so ELOOP here means link cycles), or another filesystem error. The message is deliberately sanitized with no path or errno detail.","triggerScenarios":"A parent directory of the config is not searchable by the current user (mode without x); symlink cycles (a -> b -> a); exotic filesystems returning errors from stat.","commonSituations":"Configs under permission-hardened directories; containers/sandboxes denying stat; user mismatch between the launching environment and the file owner.","solutions":["Walk the path with namei -l <path> (or ls -ld each parent) to find the first inaccessible component and relax permissions there","Run the merge as the user who owns the config tree","Resolve symlink cycles (readlink -f) and materialize a real file at the config path"],"exampleFix":"# before\nchmod 700 /home/user/.config  # owned by another user; merge runs as service account\npython merge_mcp_config.py ... # error: config_unavailable\n\n# after\nchmod o+x /home/user/.config  # or run the merge as the owning user\npython merge_mcp_config.py ...","handlingStrategy":"try-catch","validationCode":"import os\n\nos.stat(source_path)  # surfaces EACCES/ELOOP on the path or its parents before the merge runs","typeGuard":null,"tryCatchPattern":"if result.returncode != 0 and 'config_unavailable' in result.stderr:\n    # walk parents with namei -l <path>; fix the first component that denies access, then retry\n    ...","preventionTips":["Ensure every parent directory of the config is searchable (x bit) by the running user","Avoid symlink cycles in config paths","Run the migration as the user who owns the config tree"],"tags":["filesystem","permissions","config"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}