What is the correct character escaping for workflow command values e.g. echo ::error::XXXX

I’m using the “echo” approach to add annotations to give errors for test failures. I’m including the error message from the test which could contain arbitrary characters, and I couldn’t see from https://help.github.com/en/actions/reference/workflow-commands-for-github-actions what kind of escaping I need to perform on the command value?

From hacking around it looks like colons are OK but there’s something special going on with % characters (e.g. %0A produces a newline), maybe I should be doing URL encoding? But it doesn’t seem like other URL chars are encoded, so I’m not sure. Would be great if someone could definitively confirm what encoding is needed for safely representing string as workflow command values
echo “::error::This is a message with chars !*’();:@&=+$,/?# with %25 and %0A in it”

Hello – we’re trying to reproduce this on our end:

Are you able to share a copy of your workflow file?

If the repository is public, could you link that to us as well?

For more context, could you let us know what the expected behaviour of the workflow run is versus what is being outputted now?

Thank you!

Hi this isn’t a bug report so there’s nothing to repo really - I’m asking a question about how your API works so that I can make sure when I output a message using echo “::error::XXXXX" syntax the XXXXX shows up as I expect. Clearly there is some special handling for % escape sequences otherwise
echo “::error::This is a message with chars !*’();:@&=+$,/?# with %25 and %0A in it”
would result in a message containing “%25 and %0A” rather than what it actually does which is to convert %25 to a % and %0A to a newline. I don’t think that’s a bug, but I do what to know the specification for exactly what GitHub is doing with these strings as it’s not obvious. And because I don’t want to start relying on behaviour that is not officially supported. Thanks

Hi there, any input on this? Really need a definitive answer from GitHub on how this works, thanks.

1 Like

Hi, sorry for the delay.

The actions/toolkit has a way to sanitize an input into a string – could you give these commands a try?


Hi team - can you please give an example of how this might work in a workflow yml? I haven’t been able to figure it out from the docs

# Manually trigger a workflow to test the called workflow actions
name: Manual workflow

# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API. (or push if not commented out)
    # Inputs the workflow accepts.
        # Friendly description to be shown in the UI instead of 'reason'
        description: 'Reason for build'
        # Default value if no value is explicitly provided
        default: 'Test'
        # Input has to be provided for the workflow to run
        required: false

    uses: my-org/my-repo/.github/workflows/my-called-workflow.yml@MY-BRANCH
      BUILD_REASON: ${{ github.event.inputs.reason }}

from here I see I need to do something like

echo '::escapeData::${{ github.event.inputs.reason }}'

but this gives the literal value in the called wf:-

    BUILD_REASON: echo '::escapeData::Test I've escaped this'

if I remove the echo, the workflow file can’t be run at all:-

      BUILD_REASON: ::escapeData::${{ github.event.inputs.reason }}


Use workflow from
Workflow does not exist or does not have a workflow_dispatch trigger in this branch.

putting the command inside the deference of the env var gives:

      BUILD_REASON: ${{ ::escapeData::github.event.inputs.reason }}
 Check failure on line 39 in .github/workflows/manual.yml

GitHub Actions
/ Manual workflow
Invalid workflow file
The workflow is not valid. .github/workflows/manual.yml (Line: 39, Col: 21): Unexpected symbol: '::escapeData::github'. Located at position 1 within expression: ::escapeData::github.event.inputs.reason

this also fails:-

  BUILD_REASON: ${{ echo '::escape-data::github.event.inputs.reason' }}
 Check failure on line 39 in .github/workflows/manual.yml

GitHub Actions
/ Manual workflow
Invalid workflow file
The workflow is not valid. .github/workflows/manual.yml (Line: 39, Col: 21): Unrecognized named-value: 'echo'. Located at position 1 within expression: echo '::escape-data::github.event.inputs.reason'

If you’re trying to pre-process the input value, you can do that in a separate job that creates an output with the escaped data. However I’d rather do any escaping that may be necessary in the called workflow, to make it more robust.

hi @airtower-luna - could you please give an example ? the syntax seems weird in the examples in the doc. happy to move into the called workflow to do this, but what does that look like?

TIA :slight_smile:

Something like this, I used actions/github-script to directly run Javascript code. You could do the same in any other programming language via run, but this way I could just copy the escape code from actions/toolkit. I’d have preferred to call the function, but the “For internal use, subject to change” comment made that look questionable. :wink:

thank you, I’m going to give this a try now. Just for my knowledge on how to use toolkit actions though, let’s say I wasn’t worried about the “For internal use, subject to change” comment

what would the correct workflow call loo like using the built in function?


@airtower-luna - i tried your example and the echo was empty

where are the step outputs supposed to be set?


Hi Support! do you have an example of this being used somewhere?

The TEST environment variable is empty when the replacement is done, so of course the output will be empty, too:

The reason is here, where the parameter for the reusable workflow is set. github.events doesn’t exist, I assume you mean github.event, as in the example.