{"record":{"id":"dc06a0533aa7c2e9","repo":"BloopAI/vibe-kanban","slug":"repository-already-attached-to-workspace","errorCode":null,"errorMessage":"Repository already attached to workspace","messagePattern":"Repository already attached to workspace","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/local-deployment/src/container.rs","lineNumber":149,"sourceCode":"\n        container\n    }\n\n    fn map_workspace_manager_error(err: WorkspaceError) -> ContainerError {\n        match err {\n            WorkspaceError::Database(err) => ContainerError::Sqlx(err),\n            WorkspaceError::Worktree(err) => ContainerError::Worktree(err),\n            WorkspaceError::GitService(err) => ContainerError::GitServiceError(err),\n            WorkspaceError::Io(err) => ContainerError::Io(err),\n            WorkspaceError::NoRepositories => {\n                ContainerError::Other(anyhow!(\"No repositories provided\"))\n            }\n            WorkspaceError::Repo(err) => ContainerError::Other(anyhow!(err)),\n            WorkspaceError::WorkspaceNotFound => {\n                ContainerError::Other(anyhow!(\"Workspace not found\"))\n            }\n            WorkspaceError::RepoAlreadyAttached => {\n                ContainerError::Other(anyhow!(\"Repository already attached to workspace\"))\n            }\n            WorkspaceError::BranchNotFound { repo_name, branch } => ContainerError::Other(anyhow!(\n                \"Branch '{}' does not exist in repository '{}'\",\n                branch,\n                repo_name\n            )),\n            WorkspaceError::PartialCreation(msg) => ContainerError::Other(anyhow!(msg)),\n        }\n    }\n\n    async fn workspace_repo_inputs(\n        &self,\n        workspace_id: Uuid,\n    ) -> Result<(Vec<Repo>, Vec<RepoWorkspaceInput>), ContainerError> {\n        let workspace_repos =\n            WorkspaceRepo::find_by_workspace_id(&self.db.pool, workspace_id).await?;\n        if workspace_repos.is_empty() {\n            return Err(ContainerError::Other(anyhow!(","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/local-deployment/src/container.rs#L131-L167","documentation":"This error is produced by map_workspace_manager_error when the workspace manager reports WorkspaceError::RepoAlreadyAttached. It means the repository being added is already associated with the target workspace, so the attach operation is rejected to preserve the one-repo-per-workspace-mapping invariant. It is a domain validation error surfaced as ContainerError::Other via anyhow.","triggerScenarios":"Calling a workspace-creation or repo-attach API (e.g. container create / add repo to workspace) with a repo_id that already has a WorkspaceRepo row pointing at that workspace_id.","commonSituations":"Retrying a create request after a partial failure that already persisted the repo attachment; double-submitting a form from the UI; replaying an idempotency-unsafe create call; scripts that add repos to workspaces without checking existing mappings.","solutions":["Check existing workspace repos (WorkspaceRepo::find_by_workspace_id) before attaching and skip if already present.","Treat the attach step as idempotent: catch this error and continue instead of failing the whole operation.","If the repo is attached to the wrong workspace, detach/delete the existing WorkspaceRepo row first, then re-attach.","Use a transaction/unique constraint so a retried create does not leave a half-created workspace."],"exampleFix":"// before\nmanager.add_repo_to_workspace(ws_id, repo_id).await?;\n// after\nif !WorkspaceRepo::find_by_workspace_id(&pool, ws_id).await?\n    .iter().any(|r| r.repo_id == repo_id) {\n    manager.add_repo_to_workspace(ws_id, repo_id).await?;\n}","handlingStrategy":"validation","validationCode":"let existing = WorkspaceRepo::find_by_workspace_id(&pool, ws_id).await?;\nif existing.iter().any(|r| r.repo_id == repo_id) {\n    return Ok(()); // already attached, nothing to do\n}","typeGuard":null,"tryCatchPattern":"match container.add_repo_to_workspace(ws_id, repo_id).await {\n    Err(e) if e.to_string().contains(\"already attached\") => Ok(()), // idempotent\n    other => other,\n}","preventionTips":["Always list existing workspace repos before attaching a new one.","Make create/attach calls idempotent on the client (skip if already attached).","Add a unique constraint on (workspace_id, repo_id) at the DB level.","Guard UI submit buttons against duplicate requests."],"tags":["workspace","duplicate-resource","validation"],"backgroundTag":"resource-already-exists","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}