Gradle publish could not create resource over SFTP

Hello,

I’m trying to publish a library to a private remote repository via sftp, but keep encountering the following error:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':lib:publishMypublicationPublicationToMyRepoRepository'.
> Failed to publish publication 'mypublication' to repository 'MyRepo'
   > Could not create resource 'sftp://<hostname>:22/repo'.
      > Permission denied

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 1s

I have tried gradle versions 7 and 8, and still encountering the same error.
I have been able to successfully scp files to the remote server and also connect manually with the remote server via sftp, but it seems gradle publish is unable to transfer files via sftp.

Is there something I’m missing here that’s preventing gradle from publishing to the remote server?
I’d appreciate any help on this.

build.gradle.kts:

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java library project to get you started.
 * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.8/userguide/building_java_projects.html in the Gradle documentation.
 */
version = "0.1.0"
group = "com.example"

plugins {
    // Apply the java-library plugin for API and implementation separation.
    `java-library`
    `maven-publish`
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation(libs.junit.jupiter)

    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

// Apply a specific Java toolchain to ease working on different environments.
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

publishing {
    publications {
        create<MavenPublication>("mypublication") {
            artifactId = "mylib"
            from(components["java"])
        }
    }
    repositories {
        maven {
            name = "MyRepo"
            url = uri("sftp://<hostname>:22/repo")
            credentials {
                username = "johndoe"
                password = "********"
            }
        }
    }
}

tasks.named<Test>("test") {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

gradle publish --info:

Initialized native services in: /home/johndoe/.gradle/native
Initialized jansi services in: /home/johndoe/.gradle/native
Received JVM installation metadata from '/usr/lib/jvm/java-17-openjdk-amd64': {JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64, JAVA_VERSION=17.0.10, JAVA_VENDOR=Private Build, RUNTIME_NAME=OpenJDK Runtime Environment, RUNTIME_VERSION=17.0.10+7-Ubuntu-122.04.1, VM_NAME=OpenJDK 64-Bit Server VM, VM_VERSION=17.0.10+7-Ubuntu-122.04.1, VM_VENDOR=Private Build, OS_ARCH=amd64}
The client will now receive all logging from the daemon (pid: 1076). The daemon log file: /home/johndoe/.gradle/daemon/8.4/daemon-1076.out.log
Starting 4th build in daemon [uptime: 5 mins 16.139 secs, performance: 99%, GC rate: 0.00/s, heap usage: 0% of 512 MiB, non-heap usage: 38% of 384 MiB]
Using 4 worker leases.
Not watching /home/johndoe/mylib since the file system is not supported
Watching the file system is configured to be enabled if available
File system watching is active
Starting Build
Settings evaluated using settings file '/home/johndoe/mylib/settings.gradle.kts'.
Skipping generation of dependency accessors for libs as it is up-to-date.
Projects loaded. Root project using build file '/home/johndoe/mylib/build.gradle'.
Included projects: [root project 'mylib', project ':lib']

> Configure project :
Evaluating root project 'mylib' using build file '/home/johndoe/mylib/build.gradle'.

> Configure project :lib
Evaluating project ':lib' using build file '/home/johndoe/mylib/lib/build.gradle.kts'.
Caching disabled for Kotlin DSL accessors for project ':lib' because:
  Build cache is disabled
Skipping Kotlin DSL accessors for project ':lib' as it is up-to-date.
All projects evaluated.
Task name matched 'publish'
Selected primary task 'publish' from project :
Path for java installation '/usr/lib/jvm/openjdk-17' (Common Linux Locations) does not contain a java executable
Path for java installation '/usr/lib/jvm/openjdk-11' (Common Linux Locations) does not contain a java executable
Path for java installation '/usr/lib/jvm/openjdk-21' (Common Linux Locations) does not contain a java executable
Tasks to be executed: [task ':lib:compileJava', task ':lib:processResources', task ':lib:classes', task ':lib:jar', task ':lib:generateMetadataFileForMypublicationPublication', task ':lib:generatePomFileForMypublicationPublication', task ':lib:publishMypublicationPublicationToMyRepoRepository', task ':lib:publish']
Tasks that were excluded: []
Resolve mutations for :lib:compileJava (Thread[Execution worker,5,main]) started.
:lib:compileJava (Thread[Execution worker,5,main]) started.

> Task :lib:compileJava UP-TO-DATE
Caching disabled for task ':lib:compileJava' because:
  Build cache is disabled
Skipping task ':lib:compileJava' as it is up-to-date.
Resolve mutations for :lib:processResources (Thread[included builds,5,main]) started.
:lib:processResources (Thread[included builds,5,main]) started.

> Task :lib:processResources NO-SOURCE
Skipping task ':lib:processResources' as it has no source files and no previous output files.
Resolve mutations for :lib:classes (Thread[included builds,5,main]) started.
:lib:classes (Thread[included builds,5,main]) started.

> Task :lib:classes UP-TO-DATE
Skipping task ':lib:classes' as it has no actions.
Resolve mutations for :lib:jar (Thread[included builds,5,main]) started.
:lib:jar (Thread[included builds,5,main]) started.

> Task :lib:jar UP-TO-DATE
Caching disabled for task ':lib:jar' because:
  Build cache is disabled
Skipping task ':lib:jar' as it is up-to-date.
Resolve mutations for :lib:generateMetadataFileForMypublicationPublication (Thread[included builds,5,main]) started.
:lib:generateMetadataFileForMypublicationPublication (Thread[included builds,5,main]) started.

> Task :lib:generateMetadataFileForMypublicationPublication
Caching disabled for task ':lib:generateMetadataFileForMypublicationPublication' because:
  Build cache is disabled
Task ':lib:generateMetadataFileForMypublicationPublication' is not up-to-date because:
  Task.upToDateWhen is false.
Resolve mutations for :lib:generatePomFileForMypublicationPublication (Thread[Execution worker Thread 3,5,main]) started.
:lib:generatePomFileForMypublicationPublication (Thread[Execution worker Thread 2,5,main]) started.

> Task :lib:generatePomFileForMypublicationPublication
Caching disabled for task ':lib:generatePomFileForMypublicationPublication' because:
  Build cache is disabled
Task ':lib:generatePomFileForMypublicationPublication' is not up-to-date because:
  Task is untracked because: Gradle doesn't understand the data structures used to configure this task
Resolve mutations for :lib:publishMypublicationPublicationToMyRepoRepository (Thread[Execution worker Thread 2,5,main]) started.
:lib:publishMypublicationPublicationToMyRepoRepository (Thread[Execution worker Thread 2,5,main]) started.

> Task :lib:publishMypublicationPublicationToMyRepoRepository FAILED
Caching disabled for task ':lib:publishMypublicationPublicationToMyRepoRepository' because:
  Build cache is disabled
Task ':lib:publishMypublicationPublicationToMyRepoRepository' is not up-to-date because:
  Task has not declared any outputs despite executing actions.
Publishing to repository 'MyRepo' (sftp://<hostname>:22/repo)
Uploading mylib-0.1.0.jar to /repo/com/example/mylib/0.1.0/mylib-0.1.0.jar

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':lib:publishMypublicationPublicationToMyRepoRepository'.
> Failed to publish publication 'mypublication' to repository 'MyRepo'
   > Could not create resource 'sftp://<hostname>:22/repo'.
      > Permission denied

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 1s

5 actionable tasks: 3 executed, 2 up-to-date

You should try with Gradle 8.7 or 8.8 as since 8.7-rc-1 SSHD was upgraded and jsch replaced with this PR: Upgrade Apache SSHD and replace jsch by ljacomet · Pull Request #27777 · gradle/gradle · GitHub

If it does not work with that, maybe it is related to Missing features in sftp support of maven-publish · Issue #16092 · gradle/gradle · GitHub

Hi @Vampire

I’m currently using Gradle 8.8 and the issue still persists.

Missing features in sftp support of maven-publish · Issue #16092 · gradle/gradle · GitHub seems to be about private key authentication with SFTP, which still hasn’t been implemented. But I’m currently using password-based authentication instead, and even that fails to upload.

My current workaround is to publish to the local filesystem and manually upload to the remote server (Missing features in sftp support of maven-publish · Issue #16092 · gradle/gradle · GitHub). But I was hoping that wouldn’t be necessary.

I’ve tried publishing to two different remote servers, so I don’t think the issue is with the server.

Check with --stacktrace or better --scan where the error is coming from and what exactly it is.
The message can be misleading, but as it says “Permission denied” it could suggest that the user you authenticate with misses some permission or you are not authenticating with the user you think you are.
Or maybe you can find something in the logs of the sftp server.
Or maybe the the --debug output sheds some light on the situation.