{"record":{"id":"4c76a4056a691f47","repo":"usestrix/strix","slug":"two-workspace-files-target-workspace-workspace-r","errorCode":null,"errorMessage":"Two workspace files target /workspace/{workspace_rel}: '{seen[workspace_rel]}' and '{source}'","messagePattern":"Two workspace files target /workspace/(.+?): '(.+?)' and '(.+?)'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"strix/interface/utils.py","lineNumber":1737,"sourceCode":"    ``/workspace``; it defaults to the file name. Raises ``ValueError`` with a\n    user-facing message when a spec is unusable.\n    \"\"\"\n    resolved: list[dict[str, str]] = []\n    seen: dict[str, str] = {}\n    for spec in specs or []:\n        raw, sep, dest = spec.rpartition(\":\")\n        source_text = raw if sep and dest.strip() else spec\n        source = Path(source_text.strip()).expanduser()\n        if not source.is_file():\n            raise ValueError(f\"'{source}' is not an existing file\")\n        try:\n            with source.open(\"rb\"):\n                pass\n        except OSError as error:\n            raise ValueError(f\"Cannot read '{source}': {error}\") from error\n        workspace_rel = _workspace_file_dest(spec, source)\n        if workspace_rel in seen:\n            raise ValueError(\n                f\"Two workspace files target /workspace/{workspace_rel}: \"\n                f\"'{seen[workspace_rel]}' and '{source}'\"\n            )\n        seen[workspace_rel] = str(source)\n        resolved.append(\n            {\n                \"source_path\": str(source.resolve()),\n                \"workspace_path\": f\"/workspace/{workspace_rel}\",\n            }\n        )\n    return resolved\n\n\ndef read_workspace_files(workspace_files: list[dict[str, str]] | None) -> list[dict[str, Any]]:\n    \"\"\"Read resolved workspace files into engine ``extra_files`` entries.\"\"\"\n    entries: list[dict[str, Any]] = []\n    for workspace_file in workspace_files or []:\n        source = Path(workspace_file[\"source_path\"])","sourceCodeStart":1719,"sourceCodeEnd":1755,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/utils.py#L1719-L1755","documentation":"Raised by resolve_workspace_files() when two --workspace-file specs resolve to the same destination inside /workspace. The 'seen' dict maps each workspace-relative destination to its first source; a second spec targeting the same path is rejected because upload order would silently decide which file wins.","triggerScenarios":"Passing two specs with the same explicit destination: --workspace-file a.conf:app.conf --workspace-file b.conf:app.conf. Also triggered implicitly when two different source files share a basename: --workspace-file dir1/config.yaml --workspace-file dir2/config.yaml — both default to /workspace/config.yaml.","commonSituations":"Injecting configs from multiple directories whose files have identical names (config.yaml, .env, Dockerfile); merging specs from multiple config sources (CLI flags + config file) that both add the same file; forgetting that omitting DEST uses the source basename, colliding across directories.","solutions":["Give each colliding file an explicit distinct destination: --workspace-file dir1/config.yaml:svc1/config.yaml --workspace-file dir2/config.yaml:svc2/config.yaml","Rename one source file before the run so default basenames differ","Deduplicate merged spec lists before invoking strix (see validationCode below)"],"exampleFix":"# before\nstrix --workspace-file dir1/config.yaml --workspace-file dir2/config.yaml -t ./   # both -> /workspace/config.yaml\n# after\nstrix --workspace-file dir1/config.yaml:svc1/config.yaml --workspace-file dir2/config.yaml:svc2/config.yaml -t ./","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef dedupe_specs(specs: list[str]) -> list[str]:\n    seen: dict[str, str] = {}\n    for spec in specs:\n        raw, sep, dest = spec.rpartition(\":\")\n        key = dest.strip() if sep and dest.strip() else Path(spec).name\n        if key in seen:\n            raise ValueError(f\"duplicate destination {key}: {seen[key]} vs {spec}\")\n        seen[key] = spec\n    return specs","typeGuard":null,"tryCatchPattern":"try:\n    resolve_workspace_files(specs)\nexcept ValueError as e:\n    if 'Two workspace files target' in str(e):\n        assign_explicit_destinations_and_retry()\n    else:\n        raise","preventionTips":["Always set an explicit :DEST when injecting same-named files from different dirs","Deduplicate merged spec lists (CLI + config file) before the run","Mirror intended subdirectory structure in DEST to keep names unique"],"tags":["strix","cli","conflict","validation","workspace-files"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}