Skip to content

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

Description

@dawsontoth

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 <project>
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):

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
"<repo> 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    rumFrom real user monitoring where we aim to keep users happy

    Type

    Fields

    Priority

    P2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions