diff --git a/src/simutrans/script/api/api_command.cc b/src/simutrans/script/api/api_command.cc
index 4f1aaa390..68788f9d1 100644
--- a/src/simutrans/script/api/api_command.cc
+++ b/src/simutrans/script/api/api_command.cc
@@ -158,6 +158,11 @@ SQInteger command_work(HSQUIRRELVM vm)
 	 * (2) tool.work(pl, pos, param)
 	 * (3) tool.work(pl, pos, pos2, param)
 	 */
+	// only convention (3): there the last argument is the tool parameter, while with
+	// convention (2) it may well be a coordinate, as in tool.work(pl, pos, pos2)
+	if(  top > 4  &&  sq_gettype(vm, top) != OT_STRING  &&  sq_gettype(vm, top) != OT_NULL  ) {
+		return sq_raise_error(vm, "Tool parameter must be a string or null; descriptors have to be passed as <desc>.get_name()");
+	}
 	const char* default_param = top>3 ? param<const char*>::get(vm, top) : NULL;
 	koord3d     pos2          = top>4 ? param<koord3d>::get(vm, 4)       : koord3d::invalid;
 
diff --git a/tests/all_tests.nut b/tests/all_tests.nut
index 94e38e778..3650da980 100644
--- a/tests/all_tests.nut
+++ b/tests/all_tests.nut
@@ -256,6 +256,8 @@ all_tests <- [
 	test_way_tunnel_build_above_tunnel_slope,
 	test_way_tunnel_build_across_tunnel_slope,
 	test_way_tunnel_make_public,
+	test_way_tunnel_build_wrong_param_type,
+	test_way_tunnel_upgrade_interior_tile,
 	test_wayobj_build_straight,
 	test_wayobj_build_disconnected,
 	test_wayobj_upgrade_downgrade,
diff --git a/tests/tests/test_way_tunnel.nut b/tests/tests/test_way_tunnel.nut
index 7cf5b3ef5..f87ac9d38 100644
--- a/tests/tests/test_way_tunnel.nut
+++ b/tests/tests/test_way_tunnel.nut
@@ -570,3 +570,105 @@ function test_way_tunnel_make_public()
 	ASSERT_EQUAL(command_x.grid_lower(public_pl, coord3d(5, 4, 1)), null)
 	RESET_ALL_PLAYER_FUNDS()
 }
+
+
+function test_way_tunnel_build_wrong_param_type()
+{
+	local pl = player_x(0)
+	local default_tunnel = tunnel_desc_x.get_available_tunnels(wt_road)[0]
+
+	ASSERT_TRUE(default_tunnel != null)
+
+	// The tool parameter is the *name* of the descriptor, not the descriptor.
+	// Passing the object itself used to end up as a NULL default_param, which
+	// the tunnel tool then reported as "Error during initializing tool".
+	local msg = null
+	try {
+		command_x(tool_build_tunnel).work(pl, coord3d(3, 1, 0), coord3d(3, 3, 0), default_tunnel)
+	}
+	catch (e) {
+		msg = e
+	}
+	ASSERT_EQUAL(msg, "Tool parameter must be a string or null; descriptors have to be passed as <desc>.get_name()")
+
+	// nothing was built
+	ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0),
+		[
+			"........",
+			"........",
+			"........",
+			"........",
+			"........",
+			"........",
+			"........",
+			"........"
+		])
+
+	RESET_ALL_PLAYER_FUNDS()
+}
+
+
+function test_way_tunnel_upgrade_interior_tile()
+{
+	// Upgrade an existing tunnel tile by tile, the way a script walks a route:
+	// the tiles in between the portals are interior tunnelboden, i.e. they are
+	// is_tunnel() but not is_ground().
+	local pl = player_x(0)
+	local slow = null
+	local fast = null
+	foreach (d in tunnel_desc_x.get_available_tunnels(wt_road)) {
+		if (slow == null  ||  d.get_topspeed() < slow.get_topspeed()) { slow = d }
+		if (fast == null  ||  d.get_topspeed() > fast.get_topspeed()) { fast = d }
+	}
+	ASSERT_TRUE(slow.get_topspeed() < fast.get_topspeed())
+
+	ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 2, 0)), null)
+	ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 2, 0)), null)
+	ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(3, 3, 0)), null)
+	ASSERT_EQUAL(command_x.grid_raise(pl, coord3d(4, 3, 0)), null)
+
+	ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 1, 0), slow.get_name()), null)
+
+	// (3,1) and (3,3) are the portals, (3,2) is interior
+	ASSERT_TRUE(tile_x(3, 1, 0).is_ground())
+	ASSERT_TRUE(tile_x(3, 3, 0).is_ground())
+	ASSERT_TRUE(tile_x(3, 2, 0).is_tunnel())
+	ASSERT_FALSE(tile_x(3, 2, 0).is_ground())
+
+	for (local y = 1; y <= 3; y++) {
+		ASSERT_EQUAL(tile_x(3, y, 0).find_object(mo_way).get_max_speed(), slow.get_topspeed())
+	}
+
+	// upgrade in two steps, both touching the interior tile
+	ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 1, 0), coord3d(3, 2, 0), fast.get_name()), null)
+	ASSERT_EQUAL(command_x(tool_build_tunnel).work(pl, coord3d(3, 2, 0), coord3d(3, 3, 0), fast.get_name()), null)
+
+	for (local y = 1; y <= 3; y++) {
+		ASSERT_EQUAL(tile_x(3, y, 0).find_object(mo_way).get_max_speed(), fast.get_topspeed())
+		ASSERT_EQUAL(tile_x(3, y, 0).find_object(mo_tunnel).get_desc().get_name(), fast.get_name())
+	}
+
+	// clean up
+	local remover = command_x(tool_remover)
+	remover.set_flags(2)
+	ASSERT_EQUAL(remover.work(pl, coord3d(3, 1, 0)), null)
+	remover.set_flags(0)
+
+	ASSERT_WAY_PATTERN(wt_road, coord3d(0, 0, 0),
+		[
+			"........",
+			"........",
+			"........",
+			"........",
+			"........",
+			"........",
+			"........",
+			"........"
+		])
+
+	ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 2, 0)), null)
+	ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 2, 0)), null)
+	ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(3, 3, 0)), null)
+	ASSERT_EQUAL(command_x.grid_lower(pl, coord3d(4, 3, 0)), null)
+	RESET_ALL_PLAYER_FUNDS()
+}
