actions / actions/runner

String-to-number coercion differs from Number() for uppercase base prefixes and binary strings

Open
#4,724 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
6.3k
Forks
1.4k
Avg merge
1d 16h
Merged PRs (30d)
24

Description

Describe the bug

String-to-number coercion in comparisons treats uppercase hexadecimal/octal prefixes and binary prefixes differently from JavaScript's Number(), which ExpressionUtility.ParseNumber names as its reference. Lowercase hex/octal controls work.

This was measured with quoted string operands, not numeric literal parsing, on September 15, 2026:

Actions expression Actions result JavaScript equivalent
'0x1F' == 31 true Number('0x1F') === 31: true
'0X1F' == 31 false Number('0X1F') === 31: true
'0o17' == 15 true Number('0o17') === 15: true
'0O17' == 15 false Number('0O17') === 15: true
'0b101' == 5 false Number('0b101') === 5: true
'1e309' == Infinity true Number('1e309') === Infinity: true

The overflow control is included because rejection of the literal 1e309 does not imply rejection of the string '1e309' during coercion.

To reproduce

Copy the exact executed workflow into a repository you control, register it on the default branch and dispatch it. It requires no permissions, secrets, checkout or third-party actions. All six comparisons and Node equivalents execute in one job.

Example:

env:
  RESULT: ${{ '0X1F' == 31 }}
run: printf 'result=%s\n' "$RESULT"

Output:

result=false

Expected behavior

Either align these conversions with Number(), or document the intentionally supported subset and its differences so workflow and linter authors can implement the same rules.

Runner version and platform

GitHub-hosted runner 2.337.0, Ubuntu, Node.js v22.23.2. Completed run.

Source

In ExpressionUtility.ParseNumber, the hex and octal branches explicitly require lowercase x and o; there is no binary branch. After other conversions fail, the function returns Double.NaN. This explains the false comparisons rather than a workflow syntax error.

Job log output

{"input":"0x1F","comparedTo":"31","actionsEqual":"true","javascriptEqual":true}
{"input":"0X1F","comparedTo":"31","actionsEqual":"false","javascriptEqual":true}
{"input":"0o17","comparedTo":"15","actionsEqual":"true","javascriptEqual":true}
{"input":"0O17","comparedTo":"15","actionsEqual":"false","javascriptEqual":true}
{"input":"0b101","comparedTo":"5","actionsEqual":"false","javascriptEqual":true}
{"input":"1e309","comparedTo":"Infinity","actionsEqual":"true","javascriptEqual":true}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/Sdk/DTExpressions2/Expressions2/Sdk/ExpressionUtility.cs at ExpressionUtility.ParseNumber, then review the linked workflow probe and its six comparison results. Done means uppercase hexadecimal and octal prefixes and binary strings have behavior consistent with the chosen resolution: matching Number(), or having the supported differences documented clearly.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, github-actions
Domain
ci-cd, devops
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.