diff --git a/stix2/pattern_visitor.py b/stix2/pattern_visitor.py
index b2d7a53..6ac3e98 100644
--- a/stix2/pattern_visitor.py
+++ b/stix2/pattern_visitor.py
@@ -1,16 +1,12 @@
 import importlib
 import inspect
 
-from antlr4 import CommonTokenStream, InputStream
-from antlr4.tree.Trees import Trees
-import six
 from stix2patterns.exceptions import ParseException
-from stix2patterns.grammars.STIXPatternLexer import STIXPatternLexer
 from stix2patterns.grammars.STIXPatternParser import (
     STIXPatternParser, TerminalNode,
 )
 from stix2patterns.grammars.STIXPatternVisitor import STIXPatternVisitor
-from stix2patterns.validator import STIXPatternErrorListener
+from stix2patterns.v20.pattern import Pattern
 
 from .patterns import *
 from .patterns import _BooleanExpression
@@ -328,41 +324,9 @@ class STIXPatternVisitorForSTIX2(STIXPatternVisitor):
 
 def create_pattern_object(pattern, module_suffix="", module_name=""):
     """
-    Validates a pattern against the STIX Pattern grammar.  Error messages are
-    returned in a list.  The test passed if the returned list is empty.
+    Create a STIX pattern AST from a pattern string.
     """
 
-    start = ''
-    if isinstance(pattern, six.string_types):
-        start = pattern[:2]
-        pattern = InputStream(pattern)
-
-    if not start:
-        start = pattern.readline()[:2]
-        pattern.seek(0)
-
-    parseErrListener = STIXPatternErrorListener()
-
-    lexer = STIXPatternLexer(pattern)
-    # it always adds a console listener by default... remove it.
-    lexer.removeErrorListeners()
-
-    stream = CommonTokenStream(lexer)
-
-    parser = STIXPatternParser(stream)
-
-    parser.buildParseTrees = True
-    # it always adds a console listener by default... remove it.
-    parser.removeErrorListeners()
-    parser.addErrorListener(parseErrListener)
-
-    # To improve error messages, replace "<INVALID>" in the literal
-    # names with symbolic names.  This is a hack, but seemed like
-    # the simplest workaround.
-    for i, lit_name in enumerate(parser.literalNames):
-        if lit_name == u"<INVALID>":
-            parser.literalNames[i] = parser.symbolicNames[i]
-
-    tree = parser.pattern()
+    pattern_obj = Pattern(pattern)
     builder = STIXPatternVisitorForSTIX2(module_suffix, module_name)
-    return builder.visit(tree)
+    return pattern_obj.visit(builder)

Incorrect Pattern Error Propagation
When using `stix2/pattern_visitor.py/create_pattern_object()` with a pattern that does not include matching single quotes (e.g. "[ file: name = 'weirdname]" ), the stix2 library returns an unhelpful and unclear error. This is in comparison to the more useful error message provided by the pattern validator if the pattern was supplied to it directly. Thus, there should be a way for the error(s) generated by the pattern validator to be propagated back through stix2. See below stacktrace for more details.

(Based off of https://github.com/oasis-open/cti-python-stix2/issues/303)

![stacktrace](https://user-images.githubusercontent.com/43383927/73215354-f2b69680-4121-11ea-89ef-8d949202e2ed.png)

diff --git a/stix2/test/v21/test_indicator.py b/stix2/test/v21/test_indicator.py
index 152f253..23bad29 100644
--- a/stix2/test/v21/test_indicator.py
+++ b/stix2/test/v21/test_indicator.py
@@ -271,7 +271,7 @@ def test_indicator_stix20_invalid_pattern():
         )
 
     assert excinfo.value.cls == stix2.v21.Indicator
-    assert "FAIL: The same qualifier is used more than once" in str(excinfo.value)
+    assert "FAIL: Duplicate qualifier type encountered" in str(excinfo.value)
 
     ind = stix2.v21.Indicator(
         type="indicator",
diff --git a/stix2/test/v21/test_pattern_expressions.py b/stix2/test/v21/test_pattern_expressions.py
index 76880be..0c298f8 100644
--- a/stix2/test/v21/test_pattern_expressions.py
+++ b/stix2/test/v21/test_pattern_expressions.py
@@ -1,6 +1,7 @@
 import datetime
 
 import pytest
+from stix2patterns.exceptions import ParseException
 
 import stix2
 from stix2.pattern_visitor import create_pattern_object
@@ -515,3 +516,8 @@ def test_list_constant():
 def test_parsing_multiple_slashes_quotes():
     patt_obj = create_pattern_object("[ file:name = 'weird_name\\'' ]")
     assert str(patt_obj) == "[file:name = 'weird_name\\'']"
+
+
+def test_parse_error():
+    with pytest.raises(ParseException):
+        create_pattern_object("[ file: name = 'weirdname]")

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gcc
3.8
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
antlr4-python3-runtime==4.9.3
asttokens==3.0.0
attrs==25.3.0
babel==2.17.0
backcall==0.2.0
beautifulsoup4==4.13.3
bleach==6.1.0
bump2version==1.0.1
bumpversion==0.6.0
cachetools==5.5.2
certifi==2025.1.31
cfgv==3.4.0
chardet==5.2.0
charset-normalizer==3.4.1
colorama==0.4.6
coverage==7.6.1
decorator==5.2.1
defusedxml==0.7.1
distlib==0.3.9
docutils==0.17.1
exceptiongroup==1.2.2
execnet==2.1.1
executing==2.2.0
fastjsonschema==2.21.1
filelock==3.16.1
identify==2.6.1
idna==3.10
imagesize==1.4.1
importlib_metadata==8.5.0
importlib_resources==6.4.5
iniconfig==2.1.0
ipython==8.12.3
jedi==0.19.2
Jinja2==3.1.6
jsonschema==4.23.0
jsonschema-specifications==2023.12.1
jupyter_client==8.6.3
jupyter_core==5.7.2
jupyterlab_pygments==0.3.0
MarkupSafe==2.1.5
matplotlib-inline==0.1.7
mistune==3.1.3
nbclient==0.10.1
nbconvert==7.16.6
nbformat==5.10.4
nbsphinx==0.4.3
nodeenv==1.9.1
packaging==24.2
pandocfilters==1.5.1
parso==0.8.4
pexpect==4.9.0
pickleshare==0.7.5
pkgutil_resolve_name==1.3.10
platformdirs==4.3.6
pluggy==1.5.0
pre-commit==3.5.0
prompt_toolkit==3.0.50
ptyprocess==0.7.0
pure_eval==0.2.3
Pygments==2.19.1
pyproject-api==1.8.0
pytest==8.3.5
pytest-asyncio==0.24.0
pytest-cov==5.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.2
pyzmq==26.3.0
referencing==0.35.1
requests==2.32.3
rpds-py==0.20.1
simplejson==3.20.1
six==1.17.0
snowballstemmer==2.2.0
soupsieve==2.6
Sphinx==1.8.6
sphinx-prompt==1.5.0
sphinxcontrib-serializinghtml==1.1.5
sphinxcontrib-websupport==1.2.4
stack-data==0.6.3
-e git+https://github.com/oasis-open/cti-python-stix2.git@8aca39a0b037647f055ad11c85e0f289835f9f3d#egg=stix2
stix2-patterns==2.0.0
tinycss2==1.2.1
tomli==2.2.1
tornado==6.4.2
tox==4.25.0
traitlets==5.14.3
typing_extensions==4.13.0
urllib3==2.2.3
virtualenv==20.29.3
wcwidth==0.2.13
webencodings==0.5.1
zipp==3.20.2

name: cti-python-stix2
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=24.2=py38h06a4308_0
  - python=3.8.20=he870216_0
  - readline=8.2=h5eee18b_0
  - setuptools=75.1.0=py38h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.44.0=py38h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - antlr4-python3-runtime==4.9.3
      - asttokens==3.0.0
      - attrs==25.3.0
      - babel==2.17.0
      - backcall==0.2.0
      - beautifulsoup4==4.13.3
      - bleach==6.1.0
      - bump2version==1.0.1
      - bumpversion==0.6.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cfgv==3.4.0
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - colorama==0.4.6
      - coverage==7.6.1
      - decorator==5.2.1
      - defusedxml==0.7.1
      - distlib==0.3.9
      - docutils==0.17.1
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - executing==2.2.0
      - fastjsonschema==2.21.1
      - filelock==3.16.1
      - identify==2.6.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.5.0
      - importlib-resources==6.4.5
      - iniconfig==2.1.0
      - ipython==8.12.3
      - jedi==0.19.2
      - jinja2==3.1.6
      - jsonschema==4.23.0
      - jsonschema-specifications==2023.12.1
      - jupyter-client==8.6.3
      - jupyter-core==5.7.2
      - jupyterlab-pygments==0.3.0
      - markupsafe==2.1.5
      - matplotlib-inline==0.1.7
      - mistune==3.1.3
      - nbclient==0.10.1
      - nbconvert==7.16.6
      - nbformat==5.10.4
      - nbsphinx==0.4.3
      - nodeenv==1.9.1
      - packaging==24.2
      - pandocfilters==1.5.1
      - parso==0.8.4
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pkgutil-resolve-name==1.3.10
      - platformdirs==4.3.6
      - pluggy==1.5.0
      - pre-commit==3.5.0
      - prompt-toolkit==3.0.50
      - ptyprocess==0.7.0
      - pure-eval==0.2.3
      - pygments==2.19.1
      - pyproject-api==1.8.0
      - pytest==8.3.5
      - pytest-asyncio==0.24.0
      - pytest-cov==5.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.2
      - pyzmq==26.3.0
      - referencing==0.35.1
      - requests==2.32.3
      - rpds-py==0.20.1
      - simplejson==3.20.1
      - six==1.17.0
      - snowballstemmer==2.2.0
      - soupsieve==2.6
      - sphinx==1.8.6
      - sphinx-prompt==1.5.0
      - sphinxcontrib-serializinghtml==1.1.5
      - sphinxcontrib-websupport==1.2.4
      - stack-data==0.6.3
      - stix2-patterns==2.0.0
      - tinycss2==1.2.1
      - tomli==2.2.1
      - tornado==6.4.2
      - tox==4.25.0
      - traitlets==5.14.3
      - typing-extensions==4.13.0
      - urllib3==2.2.3
      - virtualenv==20.29.3
      - wcwidth==0.2.13
      - webencodings==0.5.1
      - zipp==3.20.2
prefix: /opt/conda/envs/cti-python-stix2

stix2/test/v21/test_pattern_expressions.py::test_parse_error
stix2/test/v21/test_indicator.py::test_indicator_with_all_required_properties stix2/test/v21/test_indicator.py::test_indicator_autogenerated_properties stix2/test/v21/test_indicator.py::test_indicator_type_must_be_indicator stix2/test/v21/test_indicator.py::test_indicator_id_must_start_with_indicator stix2/test/v21/test_indicator.py::test_indicator_required_properties stix2/test/v21/test_indicator.py::test_indicator_required_property_pattern stix2/test/v21/test_indicator.py::test_indicator_created_ref_invalid_format stix2/test/v21/test_indicator.py::test_indicator_revoked_invalid stix2/test/v21/test_indicator.py::test_cannot_assign_to_indicator_attributes stix2/test/v21/test_indicator.py::test_invalid_kwarg_to_indicator stix2/test/v21/test_indicator.py::test_created_modified_time_are_identical_by_default stix2/test/v21/test_indicator.py::test_parse_indicator[{\n stix2/test/v21/test_indicator.py::test_parse_indicator[data1] stix2/test/v21/test_indicator.py::test_invalid_indicator_pattern stix2/test/v21/test_indicator.py::test_indicator_with_custom_embedded_objs stix2/test/v21/test_indicator.py::test_indicator_with_custom_embed_objs_extra_props_error stix2/test/v21/test_indicator.py::test_indicator_stix20_invalid_pattern stix2/test/v21/test_pattern_expressions.py::test_create_comparison_expression stix2/test/v21/test_pattern_expressions.py::test_boolean_expression stix2/test/v21/test_pattern_expressions.py::test_boolean_expression_with_parentheses stix2/test/v21/test_pattern_expressions.py::test_hash_followed_by_registryKey_expression_python_constant stix2/test/v21/test_pattern_expressions.py::test_hash_followed_by_registryKey_expression stix2/test/v21/test_pattern_expressions.py::test_file_observable_expression stix2/test/v21/test_pattern_expressions.py::test_multiple_file_observable_expression[AndObservationExpression-AND] stix2/test/v21/test_pattern_expressions.py::test_multiple_file_observable_expression[OrObservationExpression-OR] stix2/test/v21/test_pattern_expressions.py::test_root_types stix2/test/v21/test_pattern_expressions.py::test_artifact_payload stix2/test/v21/test_pattern_expressions.py::test_greater_than_python_constant stix2/test/v21/test_pattern_expressions.py::test_greater_than stix2/test/v21/test_pattern_expressions.py::test_less_than stix2/test/v21/test_pattern_expressions.py::test_greater_than_or_equal stix2/test/v21/test_pattern_expressions.py::test_less_than_or_equal stix2/test/v21/test_pattern_expressions.py::test_not stix2/test/v21/test_pattern_expressions.py::test_and_observable_expression stix2/test/v21/test_pattern_expressions.py::test_invalid_and_observable_expression stix2/test/v21/test_pattern_expressions.py::test_hex stix2/test/v21/test_pattern_expressions.py::test_multiple_qualifiers stix2/test/v21/test_pattern_expressions.py::test_set_op stix2/test/v21/test_pattern_expressions.py::test_timestamp stix2/test/v21/test_pattern_expressions.py::test_boolean stix2/test/v21/test_pattern_expressions.py::test_binary stix2/test/v21/test_pattern_expressions.py::test_list stix2/test/v21/test_pattern_expressions.py::test_list2 stix2/test/v21/test_pattern_expressions.py::test_invalid_constant_type stix2/test/v21/test_pattern_expressions.py::test_invalid_integer_constant stix2/test/v21/test_pattern_expressions.py::test_invalid_timestamp_constant stix2/test/v21/test_pattern_expressions.py::test_invalid_float_constant stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[True-True0] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[False-False0] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[True-True1] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[False-False1] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[true-True] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[false-False] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[t-True] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[f-False] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[T-True] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[F-False] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[1-True] stix2/test/v21/test_pattern_expressions.py::test_boolean_constant[0-False] stix2/test/v21/test_pattern_expressions.py::test_invalid_boolean_constant stix2/test/v21/test_pattern_expressions.py::test_invalid_hash_constant[MD5-zzz] stix2/test/v21/test_pattern_expressions.py::test_invalid_hash_constant[ssdeep-zzz==] stix2/test/v21/test_pattern_expressions.py::test_invalid_hex_constant stix2/test/v21/test_pattern_expressions.py::test_invalid_binary_constant stix2/test/v21/test_pattern_expressions.py::test_escape_quotes_and_backslashes stix2/test/v21/test_pattern_expressions.py::test_like stix2/test/v21/test_pattern_expressions.py::test_issuperset stix2/test/v21/test_pattern_expressions.py::test_repeat_qualifier stix2/test/v21/test_pattern_expressions.py::test_invalid_repeat_qualifier stix2/test/v21/test_pattern_expressions.py::test_invalid_within_qualifier stix2/test/v21/test_pattern_expressions.py::test_startstop_qualifier stix2/test/v21/test_pattern_expressions.py::test_invalid_startstop_qualifier stix2/test/v21/test_pattern_expressions.py::test_make_constant_already_a_constant stix2/test/v21/test_pattern_expressions.py::test_parsing_comparison_expression stix2/test/v21/test_pattern_expressions.py::test_parsing_qualified_expression stix2/test/v21/test_pattern_expressions.py::test_list_constant stix2/test/v21/test_pattern_expressions.py::test_parsing_multiple_slashes_quotes
stix2/pattern_visitor.py
stix2/pattern_visitor.py