MemberJunction / MemberJunction/MJ
mj app install: a private Open App repo reports "Tag 'vX.Y.Z' not found" when the real cause is missing GitHub auth
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 323
Description
## Summary
`mj app install` against a **private** Open App repository fails with a message that blames a missing tag, when the tag exists and the real cause is that no GitHub credential was supplied. The message then points at the repo's `/tags` page — which a signed-in maintainer opens and sees the tag on, confirming the wrong conclusion.
GitHub returns **404, not 403**, for a private repository the caller cannot see. So "tag missing" and "repo not visible to this credential" arrive identically, and the installer reports only the first.
## Reproduction
Against any private Open App repo with a valid release tag (here `MemberJunction/bizapps-ats`, tag `v6.0.0`), with no `GITHUB_TOKEN` in the environment:
```
mj app install https://github.com/MemberJunction/bizapps-ats --version 6.0.0 --non-interactive --verbose
```
```
- [Fetch] Validating version tag v6.0.0 exists...
GET /repos/MemberJunction/bizapps-ats/git/ref/tags%2Fv6.0.0 - 404
✖ Install failed: Tag 'v6.0.0' not found in MemberJunction/bizapps-ats.
Available versions can be checked at https://github.com/MemberJunction/bizapps-ats/tags
```
The tag does exist. Same API call, unauthenticated vs authenticated:
| Call | Result |
|---|---|
| Unauthenticated `GET /repos/.../git/ref/tags/v6.0.0` | **404** |
| Authenticated with a repo-scoped token | **200** → `refs/tags/v6.0.0` |
`git ls-remote --tags https://github.com/MemberJunction/bizapps-ats` also lists `v6.0.0`, because local git credentials are configured — which makes the CLI's report look even more like a genuine missing tag.
Re-running the identical command with `GITHUB_TOKEN=$(gh auth token)` proceeds past tag validation, fetches the manifest, and resolves the dependency graph.
## Root cause
`packages/OpenApp/Engine/src/github/github-client.ts` → `ValidateGitHubTag` (~L722):
```ts
catch (error: unknown) {
if (OctokitStatus(error) === 404) {
return { Exists: false, ErrorMessage: `Tag '${tag}' not found in ${parsed.Owner}/${parsed.Repo}. Available versions can be checked at ${repoUrl}/tags` };
}
```
A 404 is attributed unconditionally to the tag. For a private repo without credentials the 404 is the *repository*, not the ref.
## Why it is worth fixing rather than documenting
The token is already supported — `packages/MJCLI/src/utils/open-app-context.ts:259` reads `config.openApps?.github?.token ?? process.env.GITHUB_TOKEN`. But `mj app install --help` documents only `--version`, `-v` and `--non-interactive`, so nothing in the command's own surface connects the failure to the remedy. Every first-party BizApp repo is private, so this is the first thing a maintainer hits.
## Suggested fix
Distinguish the two cases before composing the message. On a 404 during tag validation, probe repo visibility (`GET /repos/{owner}/{repo}`) and, when *that* also 404s with no credential present, say so — naming `GITHUB_TOKEN` / `openApps.github.token`. Something like:
> Cannot read `MemberJunction/bizapps-ats`. The repository is private or does not exist, and no GitHub credential was supplied — set `GITHUB_TOKEN` or `openApps.github.token` in `mj.config.cjs`.
Documenting `GITHUB_TOKEN` in `mj app install --help` would help independently of the message change.
## Environment
`@memberjunction/cli` 6.1.0, macOS, Node 24.20.0. Found while doing a clean-room `mj app install` of a newly published private Open App.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Contributor guide
Research direction
Start in packages/OpenApp/Engine/src/github/github-client.ts at ValidateGitHubTag, then inspect the token lookup in packages/MJCLI/src/utils/open-app-context.ts. Reproduce the shown private-repository install without GITHUB_TOKEN and compare it with an authenticated run. Done means a 404 caused by missing repository access produces an actionable credential message instead of claiming the tag is missing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github, node.js, typescript
- Domain
- api, authentication, cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100