laurent22/joplin · error · IOExceptionFast

The created file name was not as expected: input name was

Error message

The created file name was not as expected: input name was '${fileName}' but got: '${createdFileName}'

What it means

After SAF creates the document, the provider may adjust the name (append an extension derived from the MIME type, or dedupe a collision by appending ' (1)'). When the created file's display name differs from the requested fileName and replaceIfDestinationExists is false, the library deletes the stray file and throws instead of silently returning a differently-named document. It protects callers who need the exact name they asked for.

Solutions

  1. Pass replaceIfDestinationExists=true if overwriting the existing destination is acceptable
  2. Include the correct extension in fileName matching the mimeType (e.g. 'notes.txt' for text/plain) so the provider doesn't append one
  3. Check for the file's existence first (statFile) and pick a unique name before creating
  4. Catch the error and read the requested vs actual names from the message to reconcile your UI/state

Example fix

// before
await Safx.createFile(treeUri, 'notes', 'text/plain'); // provider creates 'notes.txt' -> throws
// after
await Safx.createFile(treeUri, 'notes.txt', 'text/plain', true);
Defensive patterns

Strategy: try-catch

Validate before calling

const existing = await Safx.statFile(treeUri, fileName);
if (existing) {
	// either replace intentionally or pick a new unique name
}
const nameWithExt = /\.[a-z0-9]+$/i.test(fileName) ? fileName : `${fileName}.txt`; // match the mimeType

Type guard

const resolvesToRequestedName = async (treeUri: string, fileName: string) => {
	try { const s = await Safx.statFile(treeUri, fileName); return !!s && !s.isDirectory; } catch { return false; }
};

Try / catch

try {
	const uri = await Safx.createFile(treeUri, fileName, mimeType);
} catch (e) {
	if (String(e?.message).startsWith('The created file name was not as expected')) {
		// retry with replaceIfDestinationExists=true or a unique name including the correct extension
	}
	throw e;
}

Prevention

When it happens

Trigger: Calling createFile/createFileInDirectory with replaceIfDestinationExists=false while name-mangling occurs: (1) target file already exists and the provider renamed the new document instead of failing, (2) the provider appends an extension for the MIME type (e.g. requesting 'notes' with mime text/plain creates 'notes.txt'), (3) provider-side normalization changes the name.

Common situations: Creating a file in a folder where the name already exists (collision dedupe by the provider); omitting the extension in the name while providing a MIME type so the provider appends it; OEM storage providers behaving differently from the AOSP DocumentsProvider.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

    );

    if (createdFile == null) {
      throw new IOExceptionFast(
        "File creation failed without any specific error for '" + fileName + "'");
    }

    String createdFileName = UriHelper.getFileName(createdFile.toString());

    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();
    }

View on GitHub (pinned to 981a03c5c9)