fix: coerce stringified booleans in fpsProperties - #163
Closed
andrewwan-uipath wants to merge 3 commits into
Closed
Conversation
from_config assigns fpsProperties onto the context with setattr, which bypasses pydantic validation. Producers that deliver fpsProperties as a string->string map therefore land "false" on a bool-typed field, where it stays a truthy non-empty string and silently inverts the guards reading it. conversationalService.endExchange is the visible case: "false" made the runtime emit the exchange-end event anyway, closing an exchange the caller asked to keep open. endExchange, enableOutputs and runAsMe all map onto bool fields and share the failure mode; booleans meant to be true were unaffected, so it went unnoticed. Parse stringified booleans when the target field is annotated bool, and warn and keep the default for anything unrecognizable rather than guessing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR hardens UiPathRuntimeContext.from_config against producers that emit fpsProperties as a string→string map, ensuring bool-typed context fields don’t silently receive truthy non-empty strings like "false" and invert runtime guards.
Changes:
- Add a small
bool-string parser and apply it duringfpsProperties→ context attribute assignment when the target field is annotated asbool. - Log a warning and keep the field default when a would-be boolean string is unrecognizable (rather than guessing).
- Add focused tests covering truthy/falsy string spellings, non-bool fps passthrough, unparseable values, and real JSON booleans; bump package version.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/uipath/runtime/context.py |
Adds _parse_bool_like and coerces stringified booleans when mapping fpsProperties onto bool fields. |
tests/test_context.py |
Adds regression tests to ensure stringified boolean fps values are correctly handled and non-bool fields remain unchanged. |
pyproject.toml |
Bumps version to 0.13.2. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
andrewwan-uipath
marked this pull request as draft
August 20, 2026 18:36
Only "true", "false" and "" are parsed. "0"/"1"/"yes"/"no"/"on"/"off" are spellings a JSON serializer never emits for a boolean, so coercing them was guessing at intent; unrecognized values now pass through with the behavior they have always had instead. Also drop the fps value from the warning. It is external input, and SonarCloud flagged interpolating it as a log-injection risk. The key and target attribute come from the internal mapping table and are enough to diagnose. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem
UiPathRuntimeContext.from_configmapsfpsPropertiesonto the context withsetattr, which bypasses pydantic validation (model_confighas novalidate_assignment). When a producer deliversfpsPropertiesas a string→string map, a JSON boolean arrives as text and is stored raw on abool-typed field."false"is a non-empty string, so it is truthy. Every guard reading the field inverts, with no error and no log.The visible case is
conversationalService.endExchange. Setting it tofalseis meant to leave the exchange open; with"false"stored,if not self.end_exchange:never fires and the runtime emits the exchange-end event anyway, closing an exchange the caller explicitly asked to keep open.Three fps keys map onto
boolfields and share the failure mode:conversationalService.endExchangeend_exchangeconversationalService.enableOutputsconversational_outputs_enabledconversationalService.runAsMeconversational_run_as_meBooleans intended to be
truewere unaffected —"true"is truthy too — which is why this went unnoticed. Only a value meant to befalseexposes it.Fix
Parse stringified booleans in the fps loop when the target field is annotated
bool. Anything unrecognizable logs a warning and keeps the field default rather than guessing.Deliberately scoped to the fps loop rather than enabling
validate_assignment=True: that flag is model-wide, and this model is assigned to loosely in ~12 places acrossfrom_env/from_configwithextra="allow"set, so turning on assignment validation risks unrelated breakage.""is treated as falsy alongside"false"/"0"/"no"/"off", so an empty fps value can't read as an opt-in.Verification
Real JSON booleans keep working unchanged, and non-
booltargets keep their raw value (conversationId: "false"stays the string"false").Tests written first and observed failing for the right reason (raw strings landing on bool fields) before the implementation. Full suite green;
ruff check,ruff format --checkandmypyclean.Note on the upstream cause
The stringification itself is a transport bug being followed up separately with the Orchestrator team — its debug/JIT path appears to rebuild
fpsPropertiesas string→string when it merges indebug.executor, so the same key arrives as a real boolean on non-debug runs and as a string on debug runs. This change makes the runtime robust either way, and stays correct once that is fixed.🤖 Generated with Claude Code