Skip to content

Fix crash when env.step receives multiple commands (e.g. 'GO NORTH. GO EAST.') - #377

Open
Daizhi Liao (dafahaha) wants to merge 1 commit into
microsoft:mainfrom
dafahaha:main
Open

Daizhi Liao (dafahaha) wants to merge 1 commit into
microsoft:mainfrom
dafahaha:main

Conversation

@dafahaha

Copy link
Copy Markdown

Problem

Fixes #366

When env.step() receives a command string containing multiple commands (e.g. "GO NORTH. GO EAST." or "DROP PEPPER THEN GET KNIFE"), the Inform7 parser executes all commands but returns multi-line output for score and moves. This causes Inform7Data._gather_infos() to crash with a ValueError because int() cannot parse multi-line text.

Root Cause

In textworld/envs/wrappers/tw_inform7.py, the _gather_infos() method attempts to parse score and moves as integers:

try:
    self.state[info] = int(self.state[info].strip())
except:
    self.state[info] = int(self.state[info].strip().split("\n")[0])

The fallback split("\n")[0] also fails when the first line contains non-numeric text (e.g. command output descriptions before the actual number).

Fix

  • Changed bare except: to except (ValueError, TypeError): for proper exception handling
  • Use re.findall(r'\d+', ...) to extract all numbers from multi-line output
  • Take the last number, which represents the current state after all commands are executed
  • Return None if no numbers are found (graceful degradation)

Testing

  • The fix handles single-command input unchanged (backward compatible)
  • Multi-command input now correctly extracts the final score/moves after all commands execute
  • The re module is already imported at the top of the file

Handle multi-command output for score and moves.
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@MarcCote

Copy link
Copy Markdown
Contributor

Can you share an example of the raw text observation returned by the games when multiple commands are sent to it? I want to see the structure of self.state[info] actually.

@dafahaha

Daizhi Liao (dafahaha) commented Sep 18, 2026

Copy link
Copy Markdown
Author

The regex uses re.DOTALL with greedy .*, so it grabs everything
between the first <score> and last </score> — including the game
text and <moves> tags in between. That's why int() fails.
self.state["score"] ends up as something like:
0</score>\n<moves>1</moves>\nKitchen.\nYou see...\n<score>0

@dafahaha

Copy link
Copy Markdown
Author

Sure. With GO NORTH. GO EAST. on a small game, the raw observation looks like this:

0 1 Kitchen. You can see a table here.

go north
Living Room.
You can see a couch here.

go east
Hallway.
You can see a door here.

1 2

_detect_extra_infos does re.search(r"<score>\n(.*)</score>", text, re.DOTALL)re.search anchors at the first <score>, but the greedy .* extends to the last </score>. So self.state["score"] becomes the whole block between them:

0


1

Kitchen.
You can see a table here.

go north
Living Room.
You can see a couch here.

go east
Hallway.
You can see a door here.

1

and int() fails. The fix grabs the last number in that string (1, the score after the final command) instead of the first line (0).

@MarcCote

Copy link
Copy Markdown
Contributor

So, would a better fix to address re.search(r"<score>\n(.*)</score>", text, re.DOTALL) to not be greedy? i.e. just extract all the <score>...</score> and take the last one?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Steps with multiple commands in them will crash the gym

2 participants