HarperFast / HarperFast/studio

[RUM] Git deploy with no root package.json shows a raw ENOENT naming a server temp path — 19 events, 4 sessions, 2 mid-onboarding

Open
#1,699 0 comments 0 reactions 0 assignees View on GitHub
rum
Dominant language
TypeScript
Stars
5
Forks
4
Avg merge
1d 8h
Merged PRs (30d)
40

Description

## Summary

Deploying an application from a git URL onto a default-configured instance fails with a raw
Node `ENOENT` naming an internal server temp path when the repository has no `package.json`
at its root. Studio shows that string verbatim, so the user is told:

> **Failed to import ``**
> `ENOENT: no such file or directory, open '/tmp/harper-git-clone-oXPS7c/package.json'`

Nothing in that names the actual problem ("this repository has no `package.json` in its root"),
and the path it does name belongs to a directory on the Harper node that the user cannot see
and that is deleted moments later.

In the last 30 days this is **19 events across 4 sessions**, and it is the single largest
`SSEOperationError` family (19 of 37 events). Two of the affected sessions reached the import
form from `/finish-setup` minutes earlier — i.e. this is happening during new-cluster onboarding.

Surfaced by the automated daily RUM review (app `f590deee-…`).

## Datadog findings

`@type:error @error.message:*harper-git-clone*` — 30d → now:

| | |
| --- | --- |
| Events | **19** |
| Sessions | **4** |
| View | `…/apps` in every case |
| `error.source` / `handling` | `console` / `handled` |
| Error type | `SSEOperationError` |

Retry behaviour per session — the users did not read this as "fix your repository", they read it
as "it broke, try again":

| Session | Attempts | Span | What happened next |
| --- | --- | --- | --- |
| A | **9** | 22:08 → 22:30 (22 min) | left `/apps`, browsed cluster + domains, no further deploy |
| B | **8** | 03:13 → 03:19 (6 min) | arrived from `/finish-setup` 8 min earlier; gave up |
| C | 1 | — | arrived from `/finish-setup` 6 min earlier |
| D | 1 | — | session ends |

Session B's first three attempts were a different failure (a malformed URL — Harper's message was
at least legible there), then it switched to a well-formed URL and hit this one 8 times.

Wider `SSEOperationError` context, same 30d window: 37 events / 5 sessions. **Every session in
RUM that attempted a git deploy in the window failed at least once**, and none shows a subsequent
success in the same session. Note the denominator limitation: a *successful* deploy emits no RUM
error, and the deploy travels over SSE to the shared `/operation` endpoint, so RUM cannot tell a
deploy request apart from any other operation. This is "5 sessions failed", not "5 of N".

## Root cause (server), confirmed

`harper` `components/Application.ts:498-499` (verified at `724c317d8`, `v5.2.4-162`):

```ts
const manifestPath = join(cloneDir, 'package.json');
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
```

`readFile` is unguarded. When the clone has no root `package.json`, Node throws exactly the
observed string — reproduced locally:

```
ENOENT: no such file or directory, open '/…/harper-git-clone-o0nMZ5/package.json'
```

This is on the default path: `packGitReferenceWithoutScripts` runs for **any** git reference
whenever install scripts are disallowed (`Application.ts:832-833`), which is the default.

The real fix belongs here — a `stat`/`existsSync` guard throwing something like
*"`` has no package.json in its root; a Harper application needs one"*. That is a one-guard
change in `HarperFast/harper`, and it would fix this for every client, not just Studio.

## Studio's side of it

Studio renders the server string verbatim in **two** places, and only one of them does any
failure-to-guidance mapping:

1. `src/features/instance/applications/components/NewApplication/useImportApplication.ts:104` —
the new-application import toast: `description: error instanceof Error ? error.message : undefined`.
This is the surface all 19 events hit. It is a **toast**, so the text fades.
2. `src/features/instance/applications/modals/RedeployApplicationModal.tsx:108` →
`DeployProgress.tsx` renders `{state.error}` in the failure block.

`DeployProgress.tsx` already establishes the pattern for exactly this problem — `SSH_ACCESS_FAILURE`
matches a known Harper failure string and adds an actionable "Manage SSH keys" link, with a comment
explaining why the mapping is deliberate. There is no equivalent for the missing-manifest failure,
and the import toast (surface 1) has no mapping at all.

## Suggested fix

Preference order:

1. **Guard the read in `HarperFast/harper`** (`components/Application.ts:499`) and throw a
descriptive error. Everything downstream then improves for free. Worth filing there.
2. **In Studio, in the meantime**, recognise the missing-manifest failure at both surfaces and
render guidance instead of the raw string — following the `SSH_ACCESS_FAILURE` precedent, and
suppressing the internal `/tmp/harper-git-clone-…` path either way. Doing surface 1 only would
repeat the asymmetry the existing SSH mapping already has.
3. Independently: the import failure is a **fading toast** for a failure the user has to act on
(edit a repository, then retry). Session A retried 9 times over 22 minutes. Consider giving the
import form the same persistent failure panel `DeployProgress` uses for redeploy.

## Notes / limitations

- No customer identifiers, e-mails, repository names, org/cluster/instance IDs, or session IDs are
included here. Session labels A–D are local to this issue.
- Timestamps are UTC.
- Redaction status: these events pre-date `redactRelayedMessage` reaching production. That shipped
in `v2.169.5` (commit `0d407cf1`, 2026-08-31); production ran `v2.163.1` and `v2.169.3` for the
whole sample and only moved to `v2.169.13` in the last day. Going forward the server text is
withheld from Error Tracking — which means **this family will stop being measurable in RUM**, and
is another reason to fix it now rather than watch it.

Contributor guide

Open the contributing guide

Research direction

Start with src/features/instance/applications/components/NewApplication/useImportApplication.ts and the DeployProgress.tsx failure handling, then inspect RedeployApplicationModal.tsx. Reproduce or trace a missing-root-package.json failure and compare both surfaces. Done means both Studio paths provide actionable missing-manifest guidance without exposing the internal temporary path.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.