Invalid input, secrets is not defined in the referenced workflow is the input check rejecting a key named secrets that it found where input names are read, which is what a secrets: line does when it is indented one level too deep and lands inside with:. There is a second message one word away, Invalid secret, and that word is the fastest diagnosis on the page: it names which of the two checks gave up, and therefore which file you should have open.

The two placements of a secrets key on a calling job, and the message each one produces
The grid on the right is the table reproduced in the body of this page. The annotation is quoted in full from pytorch/torchchat#1351.

What this error means

The run is rejected before any job starts. The Actions tab holds a run with no jobs and one annotation against a line in the calling workflow, carrying a line and a column number, and the pull request shows a check that never reports rather than one that went red. What makes this one confusing is the wording: the annotation says Invalid input while the key on the line it points at is called secrets, so the usual next move is to go hunting for a secret that does not exist. There is none to find. Another job in the same file, calling the same workflow, will often validate perfectly, because its secrets: line is one indent further left.

```Actions annotation, quoted from pytorch/torchchat#1351
Invalid workflow file: .github/workflows/run-readme-periodic.yml#L50
The workflow is not valid. .github/workflows/run-readme-periodic.yml (Line: 50, Col: 16): Invalid input, secrets is not defined in the referenced workflow.

 ## Common causes ### The secrets key is indented into the with block The cause in the quoted annotation, and in our experience the usual one when the name in the message is `secrets`. One extra level of indentation moves the key out of the job and into the input map, where it is read as an input name. The value and the spelling are untouched, so the line is correct in isolation and reviewers read straight past it. ### Another scalar job key was pulled in by the same edit Reindenting a block moves everything under it. `if:` and `name:` carry scalars and clear the schema the way `secrets: inherit` does, so either landing inside `with:` produces this message with its own name in it. Keys carrying a mapping, such as `permissions:` or `concurrency:`, never reach this check, because `with` accepts only scalar values. ### The caller passes an input the called workflow never declared The same message from the same check, with nothing to do with secrets: the key really is an input and no declaration matches it. A rename on either side does it, and catalyst/catalyst-moodle-workflows#159 is a public report of an input dropped from a shared workflow taking a caller in another repository down with it. ### The other direction, which is a different string A caller that passes secrets by name rather than inheriting is checked against `on.workflow_call.secrets` in the called workflow, and an unmatched name is reported as `Invalid secret, NAME is not defined in the referenced workflow.` One word from the message on this page, and a fix in the other file. ## How to fix it ### Read the column number, then move the key 1. Take the line and column from the annotation. The column lands on the value, not the key, so on ` secrets: inherit` it points at the first letter of `inherit`. 2. Look at what that line is nested under. `secrets:` belongs on the job, indented the same as `uses:` and `with:`, never among the entries inside it. 3. Move it out one level and leave the value alone. Nothing else about the call changes, and nothing has to be declared. 4. Check the other calling jobs in the file before you push, since a reindent rarely touches only one. ```.github/workflows/periodic.yml, corrected (illustrative) jobs: test-quantization-any: uses: ./.github/workflows/linux-job.yml secrets: inherit with: runner: linux.g5.4xlarge.nvidia.gpu gpu-arch-type: cuda 

Treat the with block as input names and nothing else

Everything else a calling job may carry sits beside with:, not in it, and GitHub publishes the closed set: "When you call a reusable workflow, you can only use the following keywords in the job containing the call". Those keys are name, uses, with, secrets, strategy, needs, if, concurrency and permissions, the same nine properties the workflow schema gives a calling job. Anything on that list is a sibling of uses:; anything that is neither on it nor a declared input has no place on that job.

```.github/workflows/release.yml (illustrative)
jobs:
release:
name: Release
needs: [build]
if: github.ref == 'refs/heads/main'
permissions:
contents: read
uses: ./.github/workflows/deploy.yml
with:
target: production
secrets: inherit

 ### Read the declared inputs before adding a key to with When the key really is an input, the legal names are in the called workflow under `on.workflow_call.inputs`, at the ref your `uses:` line pins. The syntax reference puts the obligation on the caller: "The identifier must match the name of an input defined by `on.workflow_call.inputs.<inputs_id>` in the called workflow." Read that file at that ref rather than a README, since an input removed from a shared workflow breaks callers at their next run, not at the change. ```.github/workflows/linux-job.yml, the called workflow (illustrative) on: workflow_call: inputs: runner: type: string default: ubuntu-latest secrets-env: description: 'Names of the secrets to expose to the script' type: string required: false 

For the Invalid secret form, fix the declaration or stop sending it

A named secrets: map is the strict form, and the docs state it plainly: "Any secrets that you pass must match the names defined in the called workflow." Either add the declaration to the called workflow or drop the name from the caller. Switching that job to secrets: inherit silences it by skipping the check rather than satisfying it, and hands the callee everything the caller can read, so treat that as a decision about trust rather than a repair.

```Two files, illustrative

The called workflow declares what it accepts.

on:
workflow_call:
secrets:
DEPLOY_TOKEN:
description: 'Token used by ./deploy.sh to publish the release'
required: true

The caller passes it by name.

jobs:
release:
uses: ./.github/workflows/deploy.yml
secrets:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

 > The mirror of that one, `Secret NAME is required, but not provided while calling.`, fires only where the declaration carries `required: true` and the caller sends nothing: [secrets: inherit not forwarded](https://latchkey.dev/learn/github-actions/gha-secrets-inherit-not-forwarded). ## How to prevent it - Keep `secrets:` on the same indent as `uses:` and `with:`, and reread it after any edit that moves a block. - Open the called workflow at the ref your `uses:` line pins before adding a key to `with:`. - Read `Invalid input` and `Invalid secret` as different findings, because they point at different files. - Review a reindent one calling job at a time, since one file can hold a correct call and a rejected one. ## The word after Invalid names the check, not the key The sentence comes from a single format string in `WorkflowTemplateConverter.cs` in actions/runner, and the kind of parameter is a slot in it. Exactly two calls fill that slot. The pass over the caller's `with` block passes the literal `input`, and the pass over the caller's `secrets` map passes a constant whose value is `secret`. Nothing else calls it. So `Invalid input` can only have come from the pass that compares your `with` keys against the inputs the called workflow declares, whatever the offending key happens to be named. That is the whole diagnosis of the annotation at the top of this page. A key called `secrets` was found among the values the caller supplied as inputs, the called workflow declares no input by that name, and the check reported it. The spelling is right and the key is real; it is in the wrong block. | Message | Check that emitted it | Where the key was written | What to change | | --- | --- | --- | --- | | Invalid input, secrets is not defined in the referenced workflow. | input | Inside `with:` | Move `secrets:` out to the job | | Invalid input, NAME is not defined in the referenced workflow. | input | Inside `with:` | Match a name in the callee `on.workflow_call.inputs` | | Invalid secret, NAME is not defined in the referenced workflow. | secret | Under `secrets:` on the job | Declare it in `on.workflow_call.secrets`, or stop sending it | | Secret NAME is required, but not provided while calling. | secret | Nowhere, nothing was sent | Pass it from the caller | > The first three rows are one format string with the parameter kind and the key filled in. The fourth is a separate string inside the secret check, which is why it is worded differently. ## One public file with both spellings in it The repository the annotation comes from is worth opening, because it got the same line right and wrong in the same file. At the commit in effect when that run failed, `.github/workflows/run-readme-periodic.yml` held three jobs, all calling `pytorch/test-infra/.github/workflows/linux_job.yml@main`. In `test-readme`, `secrets: inherit` is line 14, a sibling of `uses:` on line 13 and `with:` on line 15. In `test-quantization-any`, `with:` opens on line 48 and `secrets: inherit` is line 50, indented into it between `runner:` and `gpu-arch-type:`. Line 50, column 16 is where the annotation points, and column 16 is the first character of `inherit`. It is an easy edit to make, because the neighbors look right. That called workflow declares an input named `secrets-env`, and the job that validates passes it inside `with:`. A line reading `secrets:` near a line reading `secrets-env:` does not look out of place, and nothing in the caller can tell you it is. ```.github/workflows/periodic.yml (illustrative) jobs: # Correct: secrets is a job key, a sibling of uses and with. test-readme: uses: ./.github/workflows/linux-job.yml secrets: inherit with: runner: linux.g5.4xlarge.nvidia.gpu secrets-env: "HF_TOKEN_PERIODIC" # Rejected: secrets is now an input name, and no such input is declared. test-quantization-any: uses: ./.github/workflows/linux-job.yml with: runner: linux.g5.4xlarge.nvidia.gpu secrets: inherit gpu-arch-type: cuda 

Why nothing catches it until both files are read

The workflow schema in actions/runner defines the with block as a loose mapping: workflow-job-with sets the key type to a non-empty string and the value type to a scalar, and lists no permitted names, because the names come from whatever workflow you are calling. secrets: inherit inside with: is a non-empty key with a scalar value, which is everything the schema asks, so the reader that checks your file has no reason to stop on it.

The comparison that catches it needs the called workflow in hand, at whatever ref your uses: line resolves to, so anything reading only the file you edited has nothing to compare against. The scalar rule also explains a difference people trip on: permissions: pulled into with: never reaches this check, because it carries a mapping where the schema wants a scalar.

A correct secrets: inherit never reaches secret validation

Worth knowing, because it rules out a whole file at a glance. The function that checks secrets returns before doing anything when the call inherits, so neither secret message can come from a job that writes secrets: inherit in the right place. If a run was rejected and the annotation points at a line reading secrets: inherit, the problem is where the line is, not what it says.

It also means inheriting cannot settle a declaration argument. A caller that inherits is never told that it is missing a declared secret, or sending an undeclared one, because both checks sit behind that return.

```actions/runner, Conversion/WorkflowTemplateConverter.cs
// if the secrets are inherited from the caller, we do not have any workflowJob.SecretValues (i.e. explicit mapping)
// Inherited org/repo/env secrets will be stored in context variables and will be validated there
if (workflowJob.InheritSecrets)
{
return;
}

 ## Why there is no recorded run on this page Nothing here happens on a runner. GitHub compares the caller against the called workflow while it assembles the run graph, and the run behind the quoted annotation holds no jobs at all, so there is no log to capture and no machine whose behavior could have differed. A recording would be a picture of an empty run. The file that produced it is public and cited here by line, which is the part worth reading; the workflows printed here are illustrative and have never been run. ## FAQ ### Why does the error say Invalid input when the key is called secrets? Because the word names the check rather than the key. One format string in actions/runner produces both messages, and the slot is filled by whichever pass called it: the pass over `with` fills in `input`, the pass over the `secrets` map fills in `secret`. `Invalid input` beside a key named `secrets` means the key was found among the inputs, which is to say inside `with:`. ### Can secrets: inherit on its own cause this error? No. Secret validation returns immediately when a call inherits, so a job that writes `secrets: inherit` beside `uses:` is never compared against the callee's declarations at all. If a run was rejected and the annotation points at a line reading `secrets: inherit`, the line is in the wrong place, not the wrong shape. ### What is the difference between Invalid input and Invalid secret here? One word, and it tells you which block to look at. `Invalid input, NAME` means the caller supplied NAME inside `with:` and the callee declares no input by that name. `Invalid secret, NAME` means it supplied NAME under a named `secrets:` map and the callee declares no secret by that name. The rest of the sentence is identical, which is why the two get read for each other. ### Why did one job in my workflow validate and another not? Because each calling job is checked on its own against the workflow it calls. A reindent that moved one job's `secrets:` line leaves its neighbors untouched, so one file can hold a correct call and a rejected one. That is the run quoted here: three jobs calling the same reusable workflow, one of them with the key a level too deep. ## References - [actions/runner: WorkflowTemplateConverter.cs, ValidateUndefinedParameters and its two callers](https://github.com/actions/runner/blob/main/src/Sdk/WorkflowParser/Conversion/WorkflowTemplateConverter.cs) - [actions/runner: workflow-v1.0.json, the workflow-job and workflow-job-with definitions](https://github.com/actions/runner/blob/main/src/Sdk/WorkflowParser/workflow-v1.0.json) - [GitHub Actions: workflow syntax, jobs.<job_id>.with and jobs.<job_id>.secrets](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idwith) - [GitHub Actions: supported keywords for jobs that call a reusable workflow](https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations#supported-keywords-for-jobs-that-call-a-reusable-workflow) - [pytorch/torchchat#1351: the annotation quoted on this page](https://github.com/pytorch/torchchat/issues/1351)