diff --git a/packages/notte-browser/src/notte_browser/session.py b/packages/notte-browser/src/notte_browser/session.py index 4b77232ac..525a730f0 100644 --- a/packages/notte-browser/src/notte_browser/session.py +++ b/packages/notte-browser/src/notte_browser/session.py @@ -65,6 +65,7 @@ from notte_core.data.space import DataSpace, ImageData, StructuredData, TBaseModel from notte_core.errors.actions import InvalidActionError from notte_core.errors.base import NotteBaseError +from notte_core.errors.processing import CredentialFieldValidationError from notte_core.errors.provider import RateLimitError from notte_core.profiling import profiler from notte_core.space import ActionSpace @@ -545,6 +546,10 @@ async def _action_with_vault(self, action: BaseAction) -> BaseAction: outerHTML=await locator.evaluate("el => el.outerHTML"), ) return await self.vault.replace_credentials(action, attrs, snapshot) + except CredentialFieldValidationError: + # Sentinel placeholder used on an invalid target element — surface loudly instead of + # silently typing the literal placeholder string into the field. + raise except ValueError as e: # Credential field not found in vault (e.g., vault has email but action needs username) # Return original action - it will fail at execution with a clearer error diff --git a/packages/notte-core/src/notte_core/credentials/base.py b/packages/notte-core/src/notte_core/credentials/base.py index 01dc6745b..66e687968 100644 --- a/packages/notte-core/src/notte_core/credentials/base.py +++ b/packages/notte-core/src/notte_core/credentials/base.py @@ -26,7 +26,7 @@ from notte_core.common.types import TResponseFormat from notte_core.credentials.types import ValueWithPlaceholder, get_str_value from notte_core.errors.actions import NoCredentialsFoundError -from notte_core.errors.processing import InvalidPlaceholderError +from notte_core.errors.processing import CredentialFieldValidationError, InvalidPlaceholderError from notte_core.profiling import profiler from notte_core.utils.url import get_root_domain @@ -692,7 +692,12 @@ async def replace_credentials( if cred_class is MFAField and isinstance(action, FillAction): action = MultiFactorFillAction(id=action.id, value=action.value) else: - logger.trace(f"Could not validate element with attrs {attrs} for {cred_key}") + # The caller passed a known sentinel placeholder, so they clearly intended a + # credential substitution — but the targeted element doesn't satisfy the field's + # validation (e.g. a password placeholder pointed at a