Before opening, please confirm:
Operating System
Windows 11
Kiro Version
1.1.14
Bug Description
Investigated and written by Kiro, reading its own shipped bundles
(kiro.kiro-agent/dist/extension.js, extensions/git/dist/main.js) and its own
extension-host logs. Findings come from static reading of that minified code plus
the vscode.git log, so identifier names are de-minified by hand and the
scm/inputBox argument claim is inferred from behaviour rather than observed
directly — see the note in "The identity match never succeeds" below. Everything
else was verified by running it.
Environment
|
|
| Kiro |
1.1.14 (commit f694ef1b025756b1ae27ae7c3d9ed4215b0160fe, 2026-09-10) |
| kiro-agent |
1.1.28 |
| VS Code base |
1.131.0 |
| OS |
Windows 11 |
| Workspace |
multi-root, 11 folders, 14 git repositories open |
Summary
kiroAgent.generateCommitMessage reports
No staged changes for commit message
for a repository that has staged files. It is reading a different repository
than the one whose commit box was clicked, and it does so silently.
The cause is findRepositoryUri in kiro.kiro-agent/dist/extension.js:
repos = await command("git.api.getRepositories") // string[] of rootUri
if (arg?.rootUri) {
const hit = repos.find(s => s === arg.rootUri.toString())
if (hit) return hit
}
return repos[0] // <- silent fallback
Two independent problems combine:
1. The identity match never succeeds. The command is contributed to
scm/inputBox. The argument delivered there apparently does not carry a rootUri
that stringifies to any value returned by git.api.getRepositories, so find
misses and the fallback decides.
This part is inferred, not observed: the repository whose commit box was clicked
was open, listed, and the only one visible in the Source Control view, yet a
different one was used — which only happens if the find missed. Whether the
argument is undefined, a marshalled plain object whose toString() yields
[object Object], or a Uri that stringifies differently, we could not tell from
outside. You can, and it should be checked before fixing only the fallback.
Either way the fallback hides it completely: no log line, no message, no
indication that the intended repository was not found.
2. repos[0] is not a meaningful default. git.api.getRepositories returns
Model.repositories, backed by Model.openRepositories. The git extension's own
lookup sorts that array in place:
// extensions/git/src/model.ts - Model.getOpenRepository
for (const liveRepository of this.openRepositories.sort(
(a, b) => b.repository.root.length - a.repository.root.length)) { ... }
getOpenRepository runs on every visible-editor change and every quick-diff
lookup, so within seconds of startup openRepositories is permanently ordered
longest-root-path-first. repos[0] is therefore whichever repository is nested
deepest in the workspace — close to the least likely one the user means.
Effective order in the affected workspace, longest root path first:
[ 0] <workspace>/app-control-panel/app-control-panel <- chosen, index empty
[ 1] ~/.kiro/powers/repos/<power>.partial-<id>-1 <- Kiro Powers temp clones
... (six of these)
[ 6] ~/.kiro/powers/repos/<power>.partial-<id>-3
[ 7] <workspace>/main-repo/docs-submodule
[ 8] <workspace>/service-b
[ 9] <workspace>/main-repo/nested-docs
[10] <workspace>/wiki-a
[11] <workspace>/wiki-b
[12] <workspace>/tooling-c
[13] <workspace>/main-repo <- staged files, sorts LAST
The workspace-folder repository being committed to sorted last of fourteen,
because it is the shallowest path present - which is typical for a workspace-folder
root. The repository actually chosen had an empty index, so the staged-changes
check short-circuited.
The in-place sort is upstream VS Code behaviour and arguably fine there. It is only
harmful because a consumer treats repositories[0] as if it carried meaning.
Steps to reproduce
- Open a multi-root workspace with at least two git repositories, where one
repository is nested more deeply than the workspace-folder repository - e.g.
folder A/ is a repo, and B/nested/deep/ is also a repo.
- Open a file from
A/ so the SCM view targets it.
- Stage a change in
A/. Leave B/nested/deep/ with a clean index.
- Click the wand in
A's commit message box.
Expected: a commit message generated from A's staged diff.
Actual: "No staged changes for commit message".
Reproduces reliably. Which repository wins depends only on path lengths, so the
symptom appears and disappears as repositories are added or removed - including
temporary clones that Kiro Powers leaves under ~/.kiro/powers/repos/, some of
which no longer exist on disk yet remain registered.
Confirming it from logs
git.api.getDiff is called only after the staged-changes check passes, and maps
to git diff --cached:
Select-String -SimpleMatch -Pattern "diff --cached" `
-Path "$env:APPDATA\Kiro\logs\<session>\window1\exthost\vscode.git\Git.log"
Zero occurrences across 43,645 lines and 25 hours of use before the change below;
present immediately after.
Not the cause
Worth stating, because each of these misdirects diagnosis:
- Relocated git directories are irrelevant. These repositories use
--separate-git-dir to keep git internals off a synced drive.
git rev-parse --git-dir and core.worktree resolve correctly, the git
extension opens them without error, and it ran git status in the same second
the index was written.
- The git extension's state is correct. The Source Control view listed the
staged files throughout. Only the commit message generator disagreed.
- Not
git.statusLimit. git status -uall yields ~240 entries here, far under
the 10,000 default.
Suggested fix
Give the fallback the same heuristic Kiro already uses elsewhere in the same bundle
for remote-URL lookup, preferring in order:
- the repository whose root is the longest prefix of the active editor's path;
- the first workspace folder that is itself a repository root;
repos[0], if nothing else matches.
Separately, and more valuable than the fallback itself: log when the rootUri
match fails, or name the chosen repository in the progress notification. We got
an error only because the wrongly-chosen repository happened to have an empty
index. Had it held staged changes, the command would have produced a confident,
well-formed commit message describing a completely different repository, with
nothing to indicate it. That is the real severity here.
Fixing the scm/inputBox argument so the identity match works would make the
fallback almost unreachable. Both are worth doing.
Local patch
Applied to kiro.kiro-agent/dist/extension.js, inserted before the existing
return repos[0] (kept as the last resort), wrapped in try/catch so an
unexpected API shape degrades to current behaviour:
/*kiro-scm-repo-fix*/try{
let u = vscode.window.activeTextEditor?.document?.uri;
if (u && u.scheme === "file") {
let s = u.toString(),
c = repos.filter(x => s === x || s.startsWith(x.endsWith("/") ? x : x + "/"));
if (c.length) return c.sort((a, b) => b.length - a.length)[0];
}
let w = vscode.workspace.workspaceFolders;
if (w) for (let f of w) {
let m = repos.find(x => x === f.uri.toString());
if (m) return m;
}
}catch{}
return repos[0]
Verified against the full 14-repository set: an editor in the workspace-folder
repository selects that repository; an editor inside a nested submodule selects the
submodule; no active editor still selects the workspace-folder repository rather
than the deepest one. node --check passes on the patched bundle.
Every update ships a pristine bundle and removes this, so it has to be re-applied
by a SessionStart hook - which is why the fix belongs upstream.
Workaround for anyone hitting this
Open a file belonging to the repository you want before clicking the wand. That
does not help on current builds, since the active editor is not consulted - so in
practice: write the message by hand, or ask the agent to read git diff --cached
and write one. Closing unwanted repositories in the Source Control view does
not help: the deepest remaining repository still wins, and a workspace-folder
root is usually the shallowest path present.
Steps to Reproduce
- Open a multi-root workspace with at least two git repositories, where one
repository is nested more deeply than the workspace-folder repository - e.g.
folder A/ is a repo, and B/nested/deep/ is also a repo.
- Open a file from
A/ so the SCM view targets it.
- Stage a change in
A/. Leave B/nested/deep/ with a clean index.
- Click the wand in
A's commit message box.
Expected: a commit message generated from A's staged diff.
Actual: "No staged changes for commit message".
Reproduces reliably. Which repository wins depends only on path lengths, so the
symptom appears and disappears as repositories are added or removed - including
temporary clones that Kiro Powers leaves under ~/.kiro/powers/repos/, some of
which no longer exist on disk yet remain registered.
Expected Behavior
Expected: a commit message generated from A's staged diff.
Actual: "No staged changes for commit message".
Conversation ID
No response
Additional Context
No response
Before opening, please confirm:
Operating System
Windows 11
Kiro Version
1.1.14
Bug Description
Environment
f694ef1b025756b1ae27ae7c3d9ed4215b0160fe, 2026-09-10)Summary
kiroAgent.generateCommitMessagereportsfor a repository that has staged files. It is reading a different repository
than the one whose commit box was clicked, and it does so silently.
The cause is
findRepositoryUriinkiro.kiro-agent/dist/extension.js:Two independent problems combine:
1. The identity match never succeeds. The command is contributed to
scm/inputBox. The argument delivered there apparently does not carry arootUrithat stringifies to any value returned by
git.api.getRepositories, sofindmisses and the fallback decides.
This part is inferred, not observed: the repository whose commit box was clicked
was open, listed, and the only one visible in the Source Control view, yet a
different one was used — which only happens if the
findmissed. Whether theargument is
undefined, a marshalled plain object whosetoString()yields[object Object], or aUrithat stringifies differently, we could not tell fromoutside. You can, and it should be checked before fixing only the fallback.
Either way the fallback hides it completely: no log line, no message, no
indication that the intended repository was not found.
2.
repos[0]is not a meaningful default.git.api.getRepositoriesreturnsModel.repositories, backed byModel.openRepositories. The git extension's ownlookup sorts that array in place:
getOpenRepositoryruns on every visible-editor change and every quick-difflookup, so within seconds of startup
openRepositoriesis permanently orderedlongest-root-path-first.
repos[0]is therefore whichever repository is nesteddeepest in the workspace — close to the least likely one the user means.
Effective order in the affected workspace, longest root path first:
The workspace-folder repository being committed to sorted last of fourteen,
because it is the shallowest path present - which is typical for a workspace-folder
root. The repository actually chosen had an empty index, so the staged-changes
check short-circuited.
The in-place sort is upstream VS Code behaviour and arguably fine there. It is only
harmful because a consumer treats
repositories[0]as if it carried meaning.Steps to reproduce
repository is nested more deeply than the workspace-folder repository - e.g.
folder
A/is a repo, andB/nested/deep/is also a repo.A/so the SCM view targets it.A/. LeaveB/nested/deep/with a clean index.A's commit message box.Expected: a commit message generated from
A's staged diff.Actual: "No staged changes for commit message".
Reproduces reliably. Which repository wins depends only on path lengths, so the
symptom appears and disappears as repositories are added or removed - including
temporary clones that Kiro Powers leaves under
~/.kiro/powers/repos/, some ofwhich no longer exist on disk yet remain registered.
Confirming it from logs
git.api.getDiffis called only after the staged-changes check passes, and mapsto
git diff --cached:Zero occurrences across 43,645 lines and 25 hours of use before the change below;
present immediately after.
Not the cause
Worth stating, because each of these misdirects diagnosis:
--separate-git-dirto keep git internals off a synced drive.git rev-parse --git-dirandcore.worktreeresolve correctly, the gitextension opens them without error, and it ran
git statusin the same secondthe index was written.
staged files throughout. Only the commit message generator disagreed.
git.statusLimit.git status -uallyields ~240 entries here, far underthe 10,000 default.
Suggested fix
Give the fallback the same heuristic Kiro already uses elsewhere in the same bundle
for remote-URL lookup, preferring in order:
repos[0], if nothing else matches.Separately, and more valuable than the fallback itself: log when the
rootUrimatch fails, or name the chosen repository in the progress notification. We got
an error only because the wrongly-chosen repository happened to have an empty
index. Had it held staged changes, the command would have produced a confident,
well-formed commit message describing a completely different repository, with
nothing to indicate it. That is the real severity here.
Fixing the
scm/inputBoxargument so the identity match works would make thefallback almost unreachable. Both are worth doing.
Local patch
Applied to
kiro.kiro-agent/dist/extension.js, inserted before the existingreturn repos[0](kept as the last resort), wrapped intry/catchso anunexpected API shape degrades to current behaviour:
Verified against the full 14-repository set: an editor in the workspace-folder
repository selects that repository; an editor inside a nested submodule selects the
submodule; no active editor still selects the workspace-folder repository rather
than the deepest one.
node --checkpasses on the patched bundle.Every update ships a pristine bundle and removes this, so it has to be re-applied
by a
SessionStarthook - which is why the fix belongs upstream.Workaround for anyone hitting this
Open a file belonging to the repository you want before clicking the wand. That
does not help on current builds, since the active editor is not consulted - so in
practice: write the message by hand, or ask the agent to read
git diff --cachedand write one. Closing unwanted repositories in the Source Control view does
not help: the deepest remaining repository still wins, and a workspace-folder
root is usually the shallowest path present.
Steps to Reproduce
repository is nested more deeply than the workspace-folder repository - e.g.
folder
A/is a repo, andB/nested/deep/is also a repo.A/so the SCM view targets it.A/. LeaveB/nested/deep/with a clean index.A's commit message box.Expected: a commit message generated from
A's staged diff.Actual: "No staged changes for commit message".
Reproduces reliably. Which repository wins depends only on path lengths, so the
symptom appears and disappears as repositories are added or removed - including
temporary clones that Kiro Powers leaves under
~/.kiro/powers/repos/, some ofwhich no longer exist on disk yet remain registered.
Expected Behavior
Expected: a commit message generated from
A's staged diff.Actual: "No staged changes for commit message".
Conversation ID
No response
Additional Context
No response