Skip to content

Commit 86af268

Browse files
sebastianiv21claude
andcommitted
fix(git): distinguish missing default branch from SSH key misconfig
The FS git clone path mapped any JGit TransportException to INVALID_GIT_SSH_CONFIGURATION (AE-GIT-4032), so importing/connecting a repo with no default branch showed a misleading "SSH key misconfiguration" error even when the SSH key was valid. JGit throws "Remote branch 'HEAD' not found in upstream origin" in that case. Add AppsmithError GIT_DEFAULT_BRANCH_NOT_FOUND (AE-GIT-4052) with an actionable message and route that case to it in both fetchRemoteRepository overloads. No frontend change needed: the deploy-key step already renders non-4032 backend messages generically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d279bea commit 86af268

4 files changed

Lines changed: 79 additions & 3 deletions

File tree

app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,15 @@ public enum AppsmithError {
370370
"SSH key not configured",
371371
ErrorType.GIT_CONFIGURATION_ERROR,
372372
ErrorReferenceDocUrl.GIT_DEPLOY_KEY.getDocUrl()),
373+
GIT_DEFAULT_BRANCH_NOT_FOUND(
374+
400,
375+
AppsmithErrorCode.GIT_DEFAULT_BRANCH_NOT_FOUND.getCode(),
376+
"Couldn''t find a default branch in the remote repository. Set a default branch (for example main or master) "
377+
+ "in your Git provider''s repository settings, then try again.",
378+
AppsmithErrorAction.DEFAULT,
379+
"No default branch found",
380+
ErrorType.GIT_CONFIGURATION_ERROR,
381+
null),
373382
INVALID_GIT_REPO(
374383
400,
375384
AppsmithErrorCode.INVALID_GIT_REPO.getCode(),

app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public enum AppsmithErrorCode {
9696
GIT_UPSTREAM_CHANGES("AE-GIT-4048", "Git upstream changes"),
9797
INVALID_GIT_SSH_URL("AE-GIT-4050", "Invalid git ssh url"),
9898
REPOSITORY_NOT_FOUND("AE-GIT-4051", "Repository not found"),
99+
GIT_DEFAULT_BRANCH_NOT_FOUND("AE-GIT-4052", "Git default branch not found"),
99100
GIT_FILE_SYSTEM_ERROR("AE-GIT-5013", "Git file system error"),
100101
GIT_EXECUTION_TIMEOUT("AE-GIT-5014", "Git execution timeout"),
101102
GIT_GENERIC_ERROR("AE-GIT-5016", "Git generic error"),

app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ public Boolean isGitAuthInvalid(GitAuth gitAuth) {
153153
return !StringUtils.hasText(gitAuth.getPrivateKey()) || !StringUtils.hasText(gitAuth.getPublicKey());
154154
}
155155

156+
/**
157+
* JGit throws a TransportException with message "Remote branch ''HEAD'' not found in upstream origin"
158+
* when the remote repo has no checkout-able default branch. That is not an SSH problem, so we detect it
159+
* here to avoid the misleading "SSH key misconfiguration" error.
160+
*/
161+
static boolean isRemoteDefaultBranchMissing(Throwable error) {
162+
for (Throwable t = error; t != null; t = t.getCause()) {
163+
String msg = t.getMessage();
164+
if (msg != null && msg.toLowerCase().contains("not found in upstream")) {
165+
log.debug("Remote clone failed: repository has no default branch. JGit message: {}", msg);
166+
return true;
167+
}
168+
}
169+
return false;
170+
}
171+
156172
public Mono<String> fetchRemoteRepository(
157173
GitConnectDTO gitConnectDTO, GitAuth gitAuth, ArtifactJsonTransformationDTO jsonTransformationDTO) {
158174
String workspaceId = jsonTransformationDTO.getWorkspaceId();
@@ -176,8 +192,10 @@ public Mono<String> fetchRemoteRepository(
176192
.then(commonGitFileUtils.deleteLocalRepo(temporaryStorage))
177193
.flatMap(isDeleted -> {
178194
if (error instanceof TransportException) {
179-
return Mono.error(
180-
new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION));
195+
AppsmithError transportError = isRemoteDefaultBranchMissing(error)
196+
? AppsmithError.GIT_DEFAULT_BRANCH_NOT_FOUND
197+
: AppsmithError.INVALID_GIT_SSH_CONFIGURATION;
198+
return Mono.error(new AppsmithException(transportError));
181199
} else if (error instanceof InvalidRemoteException) {
182200
return Mono.error(
183201
new AppsmithException(AppsmithError.INVALID_PARAMETER, "remote url"));
@@ -260,7 +278,9 @@ public Mono<String> fetchRemoteRepository(
260278
AppsmithException appsmithException;
261279

262280
if (error instanceof TransportException) {
263-
appsmithException = new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION);
281+
appsmithException = isRemoteDefaultBranchMissing(error)
282+
? new AppsmithException(AppsmithError.GIT_DEFAULT_BRANCH_NOT_FOUND)
283+
: new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION);
264284
} else if (error instanceof InvalidRemoteException) {
265285
appsmithException = new AppsmithException(AppsmithError.INVALID_PARAMETER, "remote url");
266286
} else if (error instanceof TimeoutException) {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.appsmith.server.git.fs;
2+
3+
import org.eclipse.jgit.api.errors.TransportException;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
class GitFSServiceCEImplTest {
9+
10+
@Test
11+
void isRemoteDefaultBranchMissing_returnsTrue_forJGitNoDefaultBranchMessage() {
12+
Throwable error = new TransportException("Remote branch 'HEAD' not found in upstream origin");
13+
assertThat(GitFSServiceCEImpl.isRemoteDefaultBranchMissing(error)).isTrue();
14+
}
15+
16+
@Test
17+
void isRemoteDefaultBranchMissing_returnsTrue_whenMessageIsInCausalChain() {
18+
Throwable cause = new TransportException("Remote branch 'HEAD' not found in upstream origin");
19+
Throwable error = new RuntimeException("clone failed", cause);
20+
assertThat(GitFSServiceCEImpl.isRemoteDefaultBranchMissing(error)).isTrue();
21+
}
22+
23+
@Test
24+
void isRemoteDefaultBranchMissing_returnsFalse_forAuthFailure() {
25+
Throwable error = new TransportException("Auth fail");
26+
assertThat(GitFSServiceCEImpl.isRemoteDefaultBranchMissing(error)).isFalse();
27+
}
28+
29+
@Test
30+
void isRemoteDefaultBranchMissing_returnsFalse_forNullMessage() {
31+
Throwable error = new RuntimeException();
32+
assertThat(GitFSServiceCEImpl.isRemoteDefaultBranchMissing(error)).isFalse();
33+
}
34+
35+
@Test
36+
void isRemoteDefaultBranchMissing_isCaseInsensitive() {
37+
Throwable error = new TransportException("Remote branch 'HEAD' NOT FOUND IN UPSTREAM origin");
38+
assertThat(GitFSServiceCEImpl.isRemoteDefaultBranchMissing(error)).isTrue();
39+
}
40+
41+
@Test
42+
void isRemoteDefaultBranchMissing_returnsFalse_forOtherNotFoundMessage() {
43+
Throwable error = new TransportException("Repository not found");
44+
assertThat(GitFSServiceCEImpl.isRemoteDefaultBranchMissing(error)).isFalse();
45+
}
46+
}

0 commit comments

Comments
 (0)