hasura/graphql-engine · error

can't move, move target %v is not a subpath from=%q, to=%q

Error message

can't move, move target %v is not a subpath from=%q, to=%q

What it means

findMoveTargets computed a move whose source or destination is not a subpath of the allowed source/destination base directories, and isMoveAllowed rejected it. This is a security guard preventing a plugin's FileOperations from moving files outside the staging/install directories.

Source

Thrown at cli/plugins/move.go:84

		return nil, errors.E(op, fmt.Errorf("could not get files using a glob string: %w", err))
	}

	if len(gl) == 0 {
		return nil, errors.E(
			op,
			fmt.Errorf("no files in the plugin archive matched the glob pattern=%s", fo.From),
		)
	}

	moves := make([]move, 0, len(gl))
	for _, v := range gl {
		newPath := filepath.Join(newDir, filepath.Base(filepath.FromSlash(v)))
		// Check secure path
		m := move{from: v, to: newPath}
		if !isMoveAllowed(fromDir, toDir, m) {
			return nil, errors.E(
				op,
				fmt.Errorf(
					"can't move, move target %v is not a subpath from=%q, to=%q",
					m,
					fromDir,
					toDir,
				),
			)
		}

		moves = append(moves, m)
	}

	return moves, nil
}

func getDirectMove(fromDir, toDir string, fo FileOperation) (move, bool, error) {
	var (
		op errors.Op = "plugins.getDirectMove"
		m  move

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Audit the plugin manifest's From/To fields and remove any '..' or absolute-path segments in To
  2. Ensure To is a plain relative destination inside the install directory
  3. If you maintain the plugin index, validate FileOperations with IsSubPath before publishing
  4. Treat unexpected occurrences as a red flag: the plugin index may be tampered with — reinstall from the official index

Example fix

// before
To: "../ escape/bin"
// after
To: "bin"
Defensive patterns

Strategy: validation

Validate before calling

_, okFrom := plugins.IsSubPath(fromDir, filepath.Join(fromDir, filepath.FromSlash(fo.From)))
_, okTo := plugins.IsSubPath(toDir, filepath.Join(toDir, filepath.FromSlash(fo.To)))
if !okFrom || !okTo {
    return errors.New("FileOperation escapes allowed directories")
}

Try / catch

if err := moveFiles(...); err != nil && strings.Contains(err.Error(), "not a subpath") {
    // reject/fix manifest entry; do not blindly retry
}

Prevention

When it happens

Trigger: A FileOperation where the resolved destination (toDir + basename of the glob match) escapes toDir, e.g. To contains enough '..' segments to climb out, or fromDir/toDir resolution makes the joined path fall outside the base.

Common situations: Manifest To field like "../../elsewhere"; symlinks or odd relative paths in fromDir/toDir; a crafted malicious plugin index attempting path traversal.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/3a32bf58aa4f4b44. Report an issue: GitHub.