nextcloud/server · error · Sabre\DAV\Exception\BadRequest

Incompatible node types

Error message

Incompatible node types

What it means

Directory::moveInto() requires the moved node to be a Nextcloud connector Node; a source that is a plain IFile (e.g. FutureFile) falls back to copy+delete, but any other node implementation — typically supplied by a third-party DAV app — produces BadRequest('Incompatible node types'), HTTP 400.

Source

Thrown at apps/dav/lib/Connector/Sabre/Directory.php:405

	 * @param string $targetName New local file/collection name.
	 * @param string $fullSourcePath Full path to source node
	 * @param INode $sourceNode Source node itself
	 * @return bool
	 * @throws BadRequest
	 * @throws ServiceUnavailable
	 * @throws Forbidden
	 * @throws FileLocked
	 * @throws \Sabre\DAV\Exception\Forbidden
	 */
	#[\Override]
	public function moveInto($targetName, $fullSourcePath, INode $sourceNode) {
		if (!$sourceNode instanceof Node) {
			// it's a file of another kind, like FutureFile
			if ($sourceNode instanceof IFile) {
				// fallback to default copy+delete handling
				return false;
			}
			throw new BadRequest('Incompatible node types');
		}

		$destinationPath = $this->getPath() . '/' . $targetName;

		$targetNodeExists = $this->childExists($targetName);

		// at getNodeForPath we also check the path for isForbiddenFileOrDir
		// with that we have covered both source and destination
		if ($sourceNode instanceof Directory && $targetNodeExists) {
			throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
		}

		[$sourceDir,] = \Sabre\Uri\split($sourceNode->getPath());
		$destinationDir = $this->getPath();

		$sourcePath = $sourceNode->getPath();

		$isMovableMount = false;

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Update the app providing the node so it extends the connector Node class, or exposes IFile so the copy+delete fallback applies.
  2. As a client workaround, copy the content and delete the source instead of MOVE.
  3. Report the incompatibility to the app maintainer: core cannot move foreign node types.

Example fix

// before: HTTP 400 Incompatible node types
$targetDir->moveInto($newName, $sourcePath, $foreignNode);

// after: copy + delete fallback for non-connector nodes
if (!$foreignNode instanceof \OCA\DAV\Connector\Sabre\Node) {
    $targetDir->createFile($newName, $foreignNode->get());
    $foreignNode->delete();
} else {
    $targetDir->moveInto($newName, $sourcePath, $foreignNode);
}
Defensive patterns

Strategy: type-guard

Type guard

function isMovableOverDav(\Sabre\DAV\INode $node): bool {
    return $node instanceof \OCA\DAV\Connector\Sabre\Node
        || $node instanceof \Sabre\DAV\IFile; // copy+delete fallback applies
}

if (!isMovableOverDav($sourceNode)) {
    // copy content + delete source instead of MOVE
    $targetDir->createFile($targetName, $sourceNode->get());
    $sourceNode->delete();
} else {
    $targetDir->moveInto($targetName, $sourcePath, $sourceNode);
}

Try / catch

try {
    $moved = $targetDir->moveInto($targetName, $sourcePath, $sourceNode);
} catch (BadRequest $e) { // HTTP 400 Incompatible node types
    $moved = false; // signal Sabre to use its copy+delete fallback
}

Prevention

When it happens

Trigger: MOVE or COPY of a node provided by a custom DAV app that does not extend the connector Node class (and does not expose IFile) into a Nextcloud files directory.

Common situations: Third-party mount/virtual-filesystem apps after DAV connector refactors; nodes registered by abandoned apps that never adopted the connector Node contract; version skew between an app and the server.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/147ef061abb45ed9. Report an issue: GitHub.