alibaba/ARouter · error · IOException

Cannot create ARouter transform output directory: <parent>

Error message

Cannot create ARouter transform output directory: <parent>

What it means

Thrown by ScopedClassTransformer when writing the transform output: it tries mkdirs() on the output's parent directory and, if that fails (parent still not a directory), throws an IOException naming the directory path. This guarantees ARouter's class-registration transform has a valid destination before writing files.

Source

Thrown at arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/core/ScopedClassTransformer.groovy:40

    private static final List<String> REGISTER_INTERFACES = [
            'com/alibaba/android/arouter/facade/template/IRouteRoot',
            'com/alibaba/android/arouter/facade/template/IInterceptorGroup',
            'com/alibaba/android/arouter/facade/template/IProviderGroup'
    ].asImmutable()

    static Result transform(Collection<File> jars, Collection<File> directories, File output) {
        Map<String, Set<String>> implementations = new LinkedHashMap<>()
        REGISTER_INTERFACES.each { implementations.put(it, new TreeSet<String>()) }

        jars.each { scanJar(it, implementations) }
        directories.each { scanDirectory(it, implementations) }

        Set<String> registrations = new TreeSet<>()
        implementations.values().each { registrations.addAll(it) }

        File parent = output.parentFile
        if (!parent.isDirectory() && !parent.mkdirs()) {
            throw new IOException('Cannot create ARouter transform output directory: ' + parent)
        }

        File temporary = new File(parent, output.name + '.tmp')
        if (temporary.exists() && !temporary.delete()) {
            throw new IOException('Cannot replace temporary ARouter transform output: ' + temporary)
        }

        boolean logisticsCenterFound = false
        Set<String> writtenEntries = new HashSet<>()

        try {
            JarOutputStream jarOutput = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(temporary)))
            try {
                jars.each { File jar ->
                    logisticsCenterFound |= copyJar(jar, jarOutput, writtenEntries, registrations)
                }
                directories.each { File directory ->
                    logisticsCenterFound |= copyDirectory(directory, jarOutput, writtenEntries, registrations)

View on GitHub (pinned to 84f451d244)

Solutions

  1. Run `./gradlew clean` and rebuild so the output directory tree is recreated fresh.
  2. Check that no file exists at the reported parent path and remove/rename it.
  3. Verify the build directory and its parents are writable by the build user (CI permissions, docker user, antivirus).
Defensive patterns

Strategy: try-catch

Validate before calling

File parent = output.parentFile
if (!parent.isDirectory() && new File(parent.path).isFile()) {
    throw new IllegalStateException("Path exists as a file, blocking mkdir: " + parent)
}

Try / catch

try {
    transform(...)
} catch (IOException e) {
    if (e.message?.contains('Cannot create ARouter transform output directory')) {
        new File(e.message.split(': ').last()).let { it.delete(); it.mkdirs() }
        // then retry once
    } else throw e
}

Prevention

When it happens

Trigger: The parent directory of the transform output does not exist and File.mkdirs() returns false — e.g. a path segment is an existing file, the path is read-only, disk is full, or a race removed the directory.

Common situations: Build output directories cleaned by another process mid-build; running Gradle with unusual buildDir pointing at a read-only location (CI cache, docker volume); antivirus locking the directory on Windows.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


AI-assisted analysis of alibaba/ARouter@84f451d244 (2026-09-06). Data as JSON: /api/errors/613adda6c9f0eb2a. Report an issue: GitHub.