diff --git a/frontend/src/lib/xray/forms/transport/FinalMaskForm.tsx b/frontend/src/lib/xray/forms/transport/FinalMaskForm.tsx
index ee9a1edb0..83e3154d2 100644
--- a/frontend/src/lib/xray/forms/transport/FinalMaskForm.tsx
+++ b/frontend/src/lib/xray/forms/transport/FinalMaskForm.tsx
@@ -1,4 +1,4 @@
-import { Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
+import { AutoComplete, Button, Divider, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
import { DeleteOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { FormInstance } from 'antd/es/form';
import type { NamePath } from 'antd/es/form/interface';
@@ -205,13 +205,18 @@ function TcpMaskItem({
if (type === 'fragment') {
return (
<>
-
-
{
+ const str = typeof value === 'string' ? value.trim() : String(value ?? '').trim();
+ if (str.length === 0 || str === 'tlshello' || /^\d+-\d+$/.test(str)) {
+ return Promise.resolve();
+ }
+ return Promise.reject(new Error('Use "tlshello" or a packet range like 1-3'));
+}
+
// Walks a deep object path safely. Used inside shouldUpdate which gets
// the whole form values blob; we need to compare a deep field across
// prev/curr without crashing on missing intermediates.
diff --git a/frontend/src/pages/xray/outbounds/protocols/freedom.tsx b/frontend/src/pages/xray/outbounds/protocols/freedom.tsx
index 30816202b..bb484ac91 100644
--- a/frontend/src/pages/xray/outbounds/protocols/freedom.tsx
+++ b/frontend/src/pages/xray/outbounds/protocols/freedom.tsx
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
-import { Button, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
+import { AutoComplete, Button, Form, Input, InputNumber, Select, Switch, type FormInstance } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { OutboundDomainStrategies } from '@/schemas/primitives';
@@ -67,12 +67,24 @@ export default function FreedomFields({ form }: { form: FormInstance {
+ const str = String(value ?? '').trim();
+ // xray accepts "tlshello" or any packet-number range (#5075)
+ if (str === '' || str === 'tlshello' || /^\d+-\d+$/.test(str)) {
+ return Promise.resolve();
+ }
+ return Promise.reject(new Error('Use "tlshello" or a packet range like 1-3'));
+ },
+ }]}
>
-