Validation errors
.loha.yml goes through several checks before any pipeline runs: a YAML security pass, Zod schema validation, task-reference validation, and finally (only when a build is triggered) task parameter binding. This page lists the errors from each and the most common cause for each.
The dashboard editor, loha validate, and the MCP validate_pipeline tool all run the parser and the task-reference check. They do not check with: parameter names, which are bound only when a build starts.
So a config can validate cleanly and still fail, or worse, succeed while silently ignoring a parameter you set. See Task parameter errors.
This page is kept in lockstep with the parser source (packages/pipeline-config) by the loha-docs-sync skill. New errors added to the parser must land with a doc entry here.
YAML / document errors
Raised by the YAML security pass before the schema is even checked.
| Error | Cause | Fix |
|---|---|---|
YAML document exceeds maximum size (1MB) | The file is larger than 1 MB | Trim the file: 1 MB is a hard cap |
Custom YAML tags are not allowed | The document contains !! (e.g. !!python/...) | Remove the tag: only standard scalar/sequence/map types are allowed |
Too many YAML anchors (max 100) | More than 100 &anchor definitions | Reduce anchor reuse; this is an anti-attack limit |
Too many YAML aliases (max 100) | More than 100 *alias references | Reduce alias reuse; this is an anti-attack limit |
Invalid YAML: must be an object | The top level isn't a mapping | The document must start with version: etc., not a list or scalar |
project: not a valid key — … | The config declares a project: | Remove it. A repository's project is set by where you link it in Loha, not in the file |
| (a raw YAML syntax message) | e.g. bad indentation of a mapping entry at line 7, column 3 | Passed through verbatim from the YAML parser. Fix the indentation or quoting at the reported line |
Schema errors
Raised by Zod after the YAML parses. Each is prefixed with a path into the document, e.g. jobs.build.steps.0.name: Required.
| Error | Cause | Fix |
|---|---|---|
version: Invalid literal value, expected 1 | "1" | version missing or not 1 | Set version: '1' at the top |
name: String must contain at most 128 character(s) | Pipeline name too long | Shorten it |
name: Invalid | name has characters outside a-z A-Z 0-9 _ - | Use only alphanumerics, dashes, underscores |
Must have between 1 and 20 jobs | Zero jobs, or more than 20 | Stay within the 1–20 range |
Unrecognized key(s) in object: '<key>' | An unknown field inside jobs: or steps: | Remove it, or check for a typo in the field name |
<path>.name: Required | A step is missing its required name | Every step needs a name |
Step must have either "uses" or "run", but not both | A step set neither, or both, of uses / run | Pick exactly one |
Timeout must be a number followed by a unit: s, m, h, or d (e.g., "30m") | A timeout value isn't in the <number><unit> form | Use values like 30s, 10m, 2h, 1d |
Only jobs: and steps: reject unknown keys. The top level and the on: block silently discard them, so branch: instead of branches: produces no error at all. It parses to an empty filter, which matches every branch. See Unknown keys are dropped in silence.
Job artifact errors
Raised for artifacts:. The first group is checked when the file is validated, the second when the build is triggered, and the last while the job runs.
| Error | Cause | Fix |
|---|---|---|
Artifact path "<path>" is absolute. Use a path relative to the repository root, such as "dist/". | An artifacts.paths entry starts with / | Write the path relative to the workspace root |
Artifact path "<path>" contains a ".." segment. … | An entry climbs out of the workspace | Remove the .. segment |
Artifact path "<path>" contains a backslash. … / … contains a control character. … | An entry uses \ or a control character | Use / separators and plain characters |
Unrecognized key(s) in object: '<key>' (under artifacts) | artifacts: has a key other than paths | Keep only paths. There is no per-job expiry |
Step name "<name>" is reserved for Loha's artifact steps ("artifacts-save" and "artifacts-restore"). Rename it. | A step is named artifacts-save or artifacts-restore | Rename the step |
Job "<id>" has a name too long for artifacts (...; the limit is 45). Shorten the job id. | A job that saves or restores files has an id over 45 characters after normalising | Shorten the job id |
Jobs "<a>" and "<b>" both become the artifact name "<name>". Rename one of them. | Two job ids normalise to the same name | Rename one of the jobs |
ARTIFACTS_UNAVAILABLE: This pipeline passes files between jobs (artifacts:), but artifact storage is not available on this deployment. | The build was refused at trigger time: the deployment has no artifact storage | Ask your Loha administrator to enable artifact storage, or remove the non-empty paths |
Declared artifact path '<entry>' matched no files in job <jobId>. | A declared path or glob matched nothing when the job finished. The job fails | Fix the path, or make the job produce the files. Use paths: [] if it hands nothing on |
Job outputs are <n> bytes; the limit is <n> bytes per job. | The job's archive is over 1 GiB | Declare fewer or smaller paths |
This workspace has used its artifact storage; delete old artifacts or raise the limit. | The archive does not fit in the workspace's artifact quota | Delete old artifacts from the Artifacts page, or ask for a higher limit |
Could not restore job outputs (409 JOB_OUTPUTS_NOT_READY). | An upstream job's archive exists but never finished uploading, or its upload failed. The receiving job fails rather than running without the files | Re-run the build. If the upstream save step failed, fix that first |
Could not restore job outputs (410 JOB_OUTPUTS_GONE). | An upstream job's archive was deleted, usually after the retention window | Run a new build |
Task reference errors
Raised when a uses: value doesn't resolve to an official task. These are checked by the parser, the dashboard editor, and validate_pipeline.
| Error | Cause | Fix |
|---|---|---|
Invalid task reference "<ref>". Expected 'loha/<task-name>' or 'loha/<task-name>@v1'. | Malformed uses: value | Use the loha/<task>@v1 form |
Unknown task provider '<x>' in '<ref>'. This looks like GitHub Actions syntax. Use '<loha-task>' instead. | e.g. actions/checkout | Use the suggested Loha task |
Unknown task provider '<x>' … with no Loha task equivalent. Use a 'run:' step … | e.g. actions/setup-node | Express it as a run: step, or pick the step's image: |
Unknown task 'loha/<x>'. Available tasks: … | Task name typo, or a task that doesn't exist | Check the catalog |
Task parameter errors
Raised when the build is triggered, not during validation.
| Error | Cause | Fix |
|---|---|---|
Required parameter '<NAME>' not provided for task '<task>'. Set it in the 'with:' field of your .loha.yml step. | A required parameter is missing | Add it: the task's catalog page lists which are required |
Denied environment variable names: <list>. These cannot be overridden. | env: sets a protected variable | Rename it (see below) |
Image '<img>' is not on the allowed image list. | A step or service image: outside the allowlist | See Allowed images |
Job '<job>' service '<name>' is not a valid name. … | A service name is not a DNS label | Use lowercase letters, digits and hyphens |
Job '<job>' service '<name>' requests resource class '<class>', which is not enabled for this workspace. … | A service asks for a class your plan does not allow | Use an allowed class, or omit it for the micro default |
Job '<job>': matrix produces N combinations, which exceeds the limit of 20. … | A matrix is too wide | Reduce an axis or split the job |
Job '<job>': matrix axis '<axis>' has no values. … | A matrix axis is an empty list | Give it at least one value, or remove the axis |
Pipeline expands to N jobs, which exceeds the limit of 20. … | Jobs plus matrix instances exceed the ceiling | Reduce a matrix or split the pipeline |
<where> references '${{ matrix.<axis> }}', but no matrix axis named '<axis>' is defined for this job. | A matrix reference with no matching axis (a typo, or used in a job with no matrix) | Fix the axis name, or add it to strategy.matrix |
timeout must be a valid Kubernetes duration (e.g. 30s, 10m, 2h, 1d) | Malformed timeout at trigger time | Use the <number><unit> form |
timeout exceeds the maximum of <n>s (24h); requested <n>s | Timeout over the 24-hour ceiling | Lower it |
with: key produces no error at allParameter binding iterates the task's declared parameters. A with: key the task doesn't declare is dropped silently. The build succeeds and simply doesn't do what you asked. This is the single most common way a copied example misbehaves.
Always check the parameter names on the task's catalog page.
Protected environment variable names, rejected case-insensitively in env:: PATH, HOME, SHELL, USER, LD_PRELOAD, LD_LIBRARY_PATH, NODE_OPTIONS, NODE_PATH, PYTHONPATH, PYTHONSTARTUP, RUBYOPT, PERL5OPT, JAVA_TOOL_OPTIONS, BASH_ENV, ENV, CDPATH, IFS, LOHA_TOKEN, LOHA_API_URL.
Environment errors
Raised when the build is triggered, not during validation: they depend on the project's environments, which the parser cannot see. The build fails before any step runs. See environment variables.
| Error | Cause | Fix |
|---|---|---|
jobs.<job>.environment "<name>" is not an environment of this project. Environments: <list>. | environment: names an environment the build's project doesn't have | Use one of the listed names, or create the environment |
jobs.<job>.environment "<name>" cannot be used: this project has no environments. | The project has no environments yet | Create the environment, or remove environment: |
jobs.<job>.environment is set to "<name>", but environment-scoped variables are not enabled for this workspace. … | Environment variables are not enabled for your workspace yet | Remove environment:, or ask a workspace administrator |
The trigger's default environment is not an environment of this build's project. … | The trigger's Default environment belongs to another project | Pick a default environment from this project on the trigger |
This build targets an environment, but it has no project to look environments up in. | The build has no project | Remove environment: |
working_directory errors
working_directory must be a safe relative path, though note it is validated but not applied: every step currently runs in /workspace/source regardless. These errors still fire.
| Error | Cause | Fix |
|---|---|---|
Null bytes not allowed in working_directory | The path contains a null byte | Remove it |
Absolute paths not allowed in working_directory | The path starts with / | Use a path relative to the workspace |
Path traversal (..) not allowed in working_directory | The path contains a .. segment | Remove the traversal |
run script errors
run scripts are scanned for commands that would breach workspace isolation.
| Error | Cause | Fix |
|---|---|---|
Accessing Kubernetes service account token is not allowed | The script reads /var/run/secrets/kubernetes.io | Don't touch the service account token |
Accessing instance metadata endpoint is not allowed | The script hits 169.254.169.254 or fd00:ec2: | Don't read cloud instance metadata |
Direct Vault/OpenBao API access is not allowed in run scripts | The script calls Vault/OpenBao directly | Use Loha variables and secrets instead |
Piping remote content to shell is not allowed | The script pipes curl/wget output into a shell | Download, inspect, then run. Don't pipe to sh |
These patterns are matched textually across the whole script, so they can fire on innocent content: a script that merely mentions 169.254.169.254, or one containing both the words "vault" and "kv", is rejected with no override. Rephrase the script, or move the affected logic into a task.
The scan applies to run: scripts only. It does not inspect env: values or with: parameters, so it is a guardrail against accidents rather than a security boundary.
What's not yet covered
- Translation of error messages into Thai is on the v0.5.0 roadmap
- Live linting in the dashboard's
.loha.ymleditor catches schema errors as you type; deeper IDE-grade tooling is on the roadmap