laurent22/joplin · error · IOExceptionFast

The created file name was not as expected and the existing…

Error message

The created file name was not as expected and the existing destination could not be resolved: input name was '${fileName}' but got: '${createdFileName}'

What it means

This is the replace-capable branch of the same name-mismatch check: the provider created a document whose name differs from the requested fileName, and when the library tried to resolve the intended existing destination (findFile on the parent directory) it found nothing, or found a directory. Since it cannot rewrite the created document onto the intended destination, it deletes the stray file and throws, leaving the filesystem unchanged.

Solutions

  1. Pass a fileName that exactly matches what the provider will store, including the extension implied by mimeType
  2. Ensure the requested name is not the name of an existing directory in the target folder
  3. Create with a fresh unique name (timestamp/uuid suffix) instead of relying on replace semantics
  4. Catch this error, list the directory contents, and retry with a corrected name

Example fix

// before
await Safx.createFile(treeUri, 'backup', 'application/json', true); // provider makes 'backup.json', no 'backup' exists -> throws
// after
await Safx.createFile(treeUri, 'backup.json', 'application/json', true);
Defensive patterns

Strategy: try-catch

Validate before calling

const existing = await Safx.statFile(treeUri, fileName);
if (!existing || existing.isDirectory) {
	// destination not a regular file: don't rely on replace semantics; choose a new name
}

Try / catch

try {
	return await Safx.createFile(treeUri, fileName, mimeType, true);
} catch (e) {
	if (String(e?.message).includes('could not be resolved')) {
		const unique = `${Date.now()}-${fileName}`;
		return await Safx.createFile(treeUri, unique, mimeType, false);
	}
	throw e;
}

Prevention

When it happens

Trigger: createFile with replaceIfDestinationExists=true where the provider renamed the created document (extension appended, collision dedupe) AND findFile(parentDirOfFile, fileName) returns null or a directory — i.e. no regular file with exactly the requested name exists in the target folder.

Common situations: Requesting 'data' with a mime that makes the provider create 'data.json'; names containing characters the provider normalizes (case/whitespace) so exact-match lookup fails; requesting a name that matches an existing folder, so findFile resolves a directory and the destination cannot be treated as a file.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of laurent22/joplin@981a03c5c9 (2026-09-17). Data as JSON: /api/errors/f6f1c37056380bb4. Report an issue: GitHub.

Appendix: source

Thrown at packages/react-native-saf-x/android/src/main/java/com/reactnativesafx/utils/EfficientDocumentHelper.java:489

    if (!createdFileName.equals(fileName)) {
      // A provider can change the name when a document with the requested name
      // already exists. Do not try to rename this document into the occupied
      // destination: a rename can partially succeed before reporting an error.
      // Delete the document returned by this create call. Replacement-capable
      // callers can then resolve and overwrite the intended destination;
      // create-only callers retain their collision failure semantics.
      unlink(createdFile);
      if (!replaceIfDestinationExists) {
        throw new IOExceptionFast(
          "The created file name was not as expected: input name was '"
            + fileName
            + "' but got: '"
            + createdFileName
            + "'");
      }
      DocumentStat existingFile = findFile(parentDirOfFile, fileName);
      if (existingFile == null || existingFile.isDirectory()) {
        throw new IOExceptionFast(
          "The created file name was not as expected and the existing destination could not be resolved: input name was '"
            + fileName
            + "' but got: '"
            + createdFileName
            + "'");
      }
      return existingFile.getInternalUri();
    }

    return createdFile;
  }

  private void writeToFile(final Uri uri, final String data, final String encoding, final boolean append) throws IOException {
    byte[] bytes = GeneralHelper.stringToBytes(data, encoding);

    try (OutputStream out =
           context
             .getContentResolver()

View on GitHub (pinned to 981a03c5c9)