diff --git a/src/lua/init.c b/src/lua/init.c
index 250b92c15848fc35c9a4942da201945f89b8b870..43395c372e58fb3fecfe69167f3e6996d7efe86d 100644
--- a/src/lua/init.c
+++ b/src/lua/init.c
@@ -553,7 +553,7 @@ run_script_f(va_list ap)
 	const char *path = va_arg(ap, const char *);
 	bool interactive = va_arg(ap, int);
 	int optc = va_arg(ap, int);
-	char **optv = va_arg(ap, char **);
+	const char **optv = va_arg(ap, const char **);
 	int argc = va_arg(ap, int);
 	char **argv = va_arg(ap, char **);
 	/*
@@ -656,7 +656,7 @@ run_script_f(va_list ap)
 
 int
 tarantool_lua_run_script(char *path, bool interactive,
-			 int optc, char **optv, int argc, char **argv)
+			 int optc, const char **optv, int argc, char **argv)
 {
 	const char *title = path ? basename(path) : "interactive";
 	/*
diff --git a/src/lua/init.h b/src/lua/init.h
index 507360738f0ac01190e12f4afe7d7ec0f4217646..7fc0b1a319a89df5609e79739d62b048246bc71b 100644
--- a/src/lua/init.h
+++ b/src/lua/init.h
@@ -72,7 +72,7 @@ tarantool_lua_free();
  */
 int
 tarantool_lua_run_script(char *path, bool force_interactive,
-			 int optc, char **optv,
+			 int optc, const char **optv,
 			 int argc, char **argv);
 
 extern char *history;
diff --git a/src/main.cc b/src/main.cc
index 4766b0be8a469faa042565f89004d061e50f6e5a..c68ac2af71d4f352bd9058e19f9ce45c009a80ed 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -721,7 +721,7 @@ main(int argc, char **argv)
 	bool interactive = false;
 	/* Lua interpeter options, e.g. -e and -l */
 	int optc = 0;
-	char **optv = NULL;
+	const char **optv = NULL;
 	auto guard = make_scoped_guard([=]{ if (optc) free(optv); });
 
 	static struct option longopts[] = {
@@ -749,16 +749,13 @@ main(int argc, char **argv)
 		case 'e':
 			/* Save Lua interepter options to optv as is */
 			if (optc == 0) {
-				optv = (char **) calloc(argc, sizeof(char *));
+				optv = (const char **) calloc(argc,
+							      sizeof(optv[0]));
 				if (optv == NULL)
 					panic_syserror("No enough memory for arguments");
 			}
-			/*
-			 * The variable optind is the index of the next
-			 * element to be processed in argv.
-			 */
-			optv[optc++] = argv[optind - 2];
-			optv[optc++] = argv[optind - 1];
+			optv[optc++] = ch == 'l' ? "-l" : "-e";
+			optv[optc++] = optarg;
 			break;
 		default:
 			/* "invalid option" is printed by getopt */
diff --git a/test/app/gh-4775-crash-args-l-e.result b/test/app/gh-4775-crash-args-l-e.result
new file mode 100644
index 0000000000000000000000000000000000000000..88169f700dd93efe54c2d27b852b08517f42e9ca
--- /dev/null
+++ b/test/app/gh-4775-crash-args-l-e.result
@@ -0,0 +1,14 @@
+-- test-run result file version 2
+--
+-- gh-4775: crash on option concatenated with value.
+--
+child = io.popen('tarantool -e"print(100) os.exit()"')
+ | ---
+ | ...
+child:read()
+ | ---
+ | - '100'
+ | ...
+-- :close() is omitted, because SIGCHILD may be handled by
+-- libev instead of Lua. In that case :close() with fail with
+-- ECHILD, but it does not matter for this test.
diff --git a/test/app/gh-4775-crash-args-l-e.test.lua b/test/app/gh-4775-crash-args-l-e.test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..7dff8e8947dbc05fa11b492f8177434fc94bfc36
--- /dev/null
+++ b/test/app/gh-4775-crash-args-l-e.test.lua
@@ -0,0 +1,8 @@
+--
+-- gh-4775: crash on option concatenated with value.
+--
+child = io.popen('tarantool -e"print(100) os.exit()"')
+child:read()
+-- :close() is omitted, because SIGCHILD may be handled by
+-- libev instead of Lua. In that case :close() with fail with
+-- ECHILD, but it does not matter for this test.