diff --git a/args.go b/args.go
index ed1e70ce..15fe1612 100644
--- a/args.go
+++ b/args.go
@@ -38,6 +38,23 @@ func legacyArgs(cmd *Command, args []string) error {
 	return nil
 }
 
+// Legacy sub command arg validation has the following behaviour:
+// - commands with no subcommands can take arbitrary arguments
+// - commands with subcommands will do subcommand validity checking
+// - subcommands will always accept arbitrary arguments
+func legacySubCommandArgs(cmd *Command, args []string) error {
+	// no subcommand, always take args
+	if !cmd.HasSubCommands() {
+		return nil
+	}
+
+	// command with subcommands, do subcommand checking.
+	if len(args) > 0 {
+		return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
+	}
+	return nil
+}
+
 // NoArgs returns an error if any args are included.
 func NoArgs(cmd *Command, args []string) error {
 	if len(args) > 0 {
diff --git a/command.go b/command.go
index 4794e5eb..88ea6f0e 100644
--- a/command.go
+++ b/command.go
@@ -848,7 +848,10 @@ func (c *Command) Traverse(args []string) (*Command, []string, error) {
 
 		cmd := c.findNext(arg)
 		if cmd == nil {
-			return c, args, nil
+			if c.Runnable() {
+				return c, args, nil
+			}
+			return c, args, legacySubCommandArgs(c, args)
 		}
 
 		if err := c.ParseFlags(flags); err != nil {