# lychee-action checks your repository links and can file the breakage as an issue

> lychee-action is an Apache-2.0 GitHub Action that wraps the Rust link checker lychee, so a workflow can scan Markdown, HTML and text files, cache results, and open an issue listing every broken link. It suits documentation-heavy repositories where dead links accumulate unnoticed.

**lycheeverse/lychee-action** — Github action to check for broken links in Markdown, HTML, and text files using lychee, a fast link checker written in Rust.

- Repository: https://github.com/lycheeverse/lychee-action
- Website: https://lychee.cli.rs
- Stars: 522 · Forks: 62
- Language: Shell
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/lycheeverse-lychee-action

## What the action actually checks

The action quickly checks links in Markdown, HTML, and text files using lychee, which the project describes as a fast link checker written in Rust. The homepage is lychee.cli.rs, and the underlying checker is a separate project from the action, which matters because the action is a thin wrapper rather than a reimplementation.

The mechanism is a shell entrypoint. The repository top level is small and says what it is: an action.yml, an entrypoint.sh, a .lycheeignore, a fixtures/ directory, the licence files LICENSE-APACHE and LICENSE-MIT, a SECURITY.md and the usual .github directory. So the action is a container or runner step that installs a pinned version of the lychee binary, runs it with the arguments you pass, and writes a summary file. The primary language recorded for the repository is Shell, which is consistent with that design.

The value proposition is in the reporting, not the checking. Any project can run a link checker in CI. What this action adds is the machinery to turn the failures into something a human looks at: the output file, the job summary, and the ability to open a GitHub issue from that file.

The repository is not archived and the last push was on 2026-07-09, the same day as the v2.9.0 release, with v2.8.0 in February 2026 and v2.7.0 in October 2025. The version numbering is a useful signal here, because a major version bump in an action that wraps another tool usually means the interface or the defaults changed rather than the underlying tool.

## The full workflow, and why fail is false

The documentation gives a complete workflow file that checks all repository links once per day and creates an issue in case of errors. The important detail is in the step that matters:

```yaml
      - name: Link Checker
        id: lychee
        uses: lycheeverse/lychee-action@v2
        with:
          fail: false
```

The fail: false is deliberate, and the reason is visible a few lines down. The create-issue step is conditioned on the lychee step's exit code not being zero, and it uses peter-evans/create-issue-from-file with the content coming from ./lychee/out.md. So the action is designed to report rather than block. The run continues, the exit code is captured, and the report becomes an issue. The workflow also needs the issues: write permission, with an inline comment in the file noting it is required for peter-evans/create-issue-from-file, and the same permission applies whether you set it on the workflow or on the job.

The trigger set is worth noting too. The example runs on repository_dispatch, on workflow_dispatch for a manual run, and on a schedule with a cron of "00 18 * * *", so once a day at 18 minutes past the hour. A daily schedule on a documentation repository is a reasonable default, because link rot is slow but steady, and a nightly run would mostly re-confirm the previous result.

The action reference is pinned to a major tag, lycheeverse/lychee-action@v2, and the checkout step uses actions/checkout@v5. If your security policy requires commit pinning rather than tag pinning, that is the point at which you would swap in full commit SHAs.

## The argument table, which is the real interface

lychee arguments can be passed to the action via the args parameter, and on top of that the action supports additional arguments of its own. The table in the documentation lists both kinds together, and the second column matters because the action's own options are not lychee options.

The action-specific ones are debug, which enables debug output in the action with set -x and is described as helpful for troubleshooting. There is fail, to fail the workflow run on error, that is when the lychee exit code is not 0. There is failIfEmpty, which fails the entire pipeline if no links were found, which is a guard against a silent misconfiguration where the glob matches nothing and the run passes for the wrong reason. There is format, taking markdown or json, for the summary output format. There is jobSummary, which writes a GitHub job summary and only applies on Markdown output. There is lycheeVersion, taking a version like v0.15.0 or nightly, to overwrite the lychee version used. There is output, the summary output file path, defaulting to lychee/results.md. There is token, a custom GitHub token for API calls. And there is workingDirectory, which sets the directory lychee runs in and which also affects where output.md is created.

That last one has a subtlety the documentation spells out: the working directory determines the output location, so if you point it at website/subdir, the report lands relative to that. The documentation points at action.yml for a full list of supported arguments and their default values, which is the right thing to consult because the defaults are what you inherit when you write nothing.

A worked example shows the shape of a step with several of these combined, including a token passed from secrets, format set to json, an output path outside the repository, fail set to false, and a working directory pointing into a subdirectory. The comment in that example notes that changing the working directory changes the lychee output directory as well.

## Caching, because link checking hammers other people's servers

The cache section is motivated explicitly: to mitigate issues regarding rate limiting or to reduce stress on external resources, you can set up lychee's cache. The pattern uses the standard GitHub cache action with the path .lycheecache and a key of cache-lychee followed by the commit SHA, with a restore-keys prefix of cache-lychee-:

```yml
- name: Restore lychee cache
  uses: actions/cache@v4
  with:
    path: .lycheecache
    key: cache-lychee-${{ github.sha }}
    restore-keys: cache-lychee-
```

The caching semantics are described carefully and they are worth reading. The action compares and saves the cache based on the given key, so as long as a user triggers the CI run from the same commit, it is the same key. The first run saves the cache and subsequent runs do not update it, because it is the same commit hash. For restoring, the most recent available one is used and the commit hash does not matter.

That behaviour has a consequence you should think about. Because the key is the commit hash and the restore falls back to the most recent available cache, a scheduled run on a branch that has not changed reuses the previous result rather than re-checking the same links. If you want freshness on a schedule rather than per commit, you need a different key.

The documentation then offers the more controllable variant, splitting the restore and the save into separate steps using actions/cache/restore and actions/cache/save, and ensuring the cache is always saved even when the link check step fails, with an if: always() condition on the save step and the key taken from the restore step's cache-primary-key output. That is the pattern to copy if you want the cache to survive a failing run, since the single-step version above will not save a cache on a failed run.

## Excluding links, and the token question

To stop certain links being checked, you add a .lycheeignore file to the root of your repository. It supports regular expressions, one expression per line, and the repository carries its own .lycheeignore at the top level as an example. The ignore file is the escape hatch for the cases that would otherwise make a scheduled job noisy, and for an action whose whole output is a list of failures, noise is the failure mode.

There is a related escape hatch for authentication. If you need a token that requires permissions that are not available in the default GITHUB_TOKEN, the documentation says you can create a personal access token and pass it to the action via the token parameter. The token argument takes a custom GitHub token for API calls. That matters for private repositories, where link checks to internal services would otherwise fail on authorisation rather than on the link itself, and where the rate limits of an unauthenticated request are much lower.

There is a related project worth naming because the documentation mentions it. Used in conjunction with Create Issue From File, issues will be opened when the action finds link problems, provided you specify the issues: write permission. So the action does not create issues by itself, and the pairing with that separate action is what turns a report file into a ticket.

The action is published on the GitHub Marketplace as a broken link checker, and the project also runs its own link checking workflow on itself, with a Check Links badge in the readme. That is worth noting: a link checker repository whose own links are checked by the same mechanism is a reasonable signal that the tool is used on itself, though it is the kind of check that can be trivially satisfied by an ignore file.

## Where lychee-action is the wrong choice

The first limitation is that link checking a large repository is slow and noisy, and a scheduled job does not fix either. Every run re-requests the same outbound URLs, and the rate limiting the cache section mentions is the reason the cache exists at all. If your documentation is large and full of links to slow or rate-limited sites, a nightly job is a load you are placing on other people's servers, and the honest mitigation is a longer cache age or a less frequent schedule, not a bigger runner.

The second limitation is that a link checker only catches what a link checker can catch. It finds URLs that no longer resolve. It does not find a page that still resolves but now says something different, a link that moved without a redirect, an anchor that no longer exists, or a typo in a domain that happens to be registered to someone else. If your problem is link accuracy rather than link rot, this is the wrong instrument, and the cost of a false sense of security is that a green check becomes a signal nobody examines.

The third limitation is the dependency chain. The action runs lychee, and lychee is a separate binary with its own release cadence. The lycheeVersion argument lets you pin it, and the default is a specific version, but the action's own major version bumps track the wrapper's interface rather than the checker's behaviour, and a pinned lychee version ages. The readme also notes that the action can run a nightly build of lychee, which is a useful escape hatch and also a way to have your link check change under you without a commit.

The realistic alternative for a documentation site is to validate links at build time rather than on a schedule, checking only the links in the site you are about to deploy, with a plugin in the static site generator or a docs framework. The difference in approach is scope: a build time check sees only the pages you changed and fails the deploy, while lychee-action sees the whole repository on a timer and opens a ticket. Build time validation catches the link you just broke, which is the one you care about, and it costs nothing in scheduled runner minutes. A scheduled full repository sweep earns its place when your documentation is edited by many people over time and nobody is reviewing diffs for link changes, which is the case the issue-creating workflow is designed for.

## Conclusion

Adopt lychee-action if your repository has documentation with many outbound links and you want broken links reported on a schedule rather than discovered by readers. Do not adopt it as a blocking gate on the first run, since an existing repository usually has broken links already, and the documented workflow sets fail to false deliberately. Before you add it, run lychee locally or with fail: false first, write a .lycheeignore for the hosts you cannot check, and read action.yml for the default values, since the readme points there for the full argument list.

## FAQ

### What does lychee-action do?

It is a GitHub Action that checks links in Markdown, HTML and text files using lychee, a fast link checker written in Rust. It can write a summary file, add a job summary, and feed that file into the Create Issue From File action so broken links become a GitHub issue.

### How do I add the lychee link checker to a GitHub workflow?

Add a step using lycheeverse/lychee-action@v2, and set fail: false if you want the run to continue and report rather than fail. The documented example also grants the issues: write permission and adds a peter-evans/create-issue-from-file step conditioned on the lychee step exit code not being zero.

### Which arguments does lychee-action support?

Lychee arguments go through the args parameter. The action adds debug, fail, failIfEmpty, format for markdown or json, jobSummary, lycheeVersion, output, token and workingDirectory. The full list with default values is in action.yml, and changing workingDirectory also changes where the output file is written.

### How do I stop lychee-action from checking certain links?

Add a .lycheeignore file to the root of your repository. It supports regular expressions with one expression per line, and the repository ships its own .lycheeignore as an example.

### Is lychee-action maintained and under what licence?

The repository is not archived and the last push was on 2026-07-09, the same day as the v2.9.0 release, after v2.8.0 in February 2026 and v2.7.0 in October 2025. The project ships both LICENSE-APACHE and LICENSE-MIT files, and the action is listed on the GitHub Marketplace.

## Sources

- [License: Apache-2.0](https://github.com/lycheeverse/lychee-action/blob/master/LICENSE)
- [lycheeverse/lychee-action on GitHub](https://github.com/lycheeverse/lychee-action)
- [Project website](https://lychee.cli.rs)
- [README](https://github.com/lycheeverse/lychee-action/blob/master/README.md)
- [Releases](https://github.com/lycheeverse/lychee-action/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lycheeverse-lychee-action
