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
- Update the app providing the node so it extends the connector Node class, or exposes IFile so the copy+delete fallback applies.
- As a client workaround, copy the content and delete the source instead of MOVE.
- 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
- Third-party DAV apps must extend the connector Node or expose IFile for the copy+delete fallback.
- Fall back to copy+delete when MOVE yields 400 Incompatible node types.
- Pin app versions compatible with the server's DAV connector contract.
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
- Permission denied to rename the trashbin
- Message exceeds allowed character limit of 1000
- Invalid actor "$actorType"
- Invalid input values
- Permission denied to rename this collection
AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17).
Data as JSON: /api/errors/147ef061abb45ed9.
Report an issue: GitHub.