hashgraph / hashgraph/guardian

Trust Chain always fails in dry-run with "Unable to get virtual file", shown to the user as "An unexpected error occurred"

Open
#6,851 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
146
Forks
186
Avg merge
2d 20h
Merged PRs (30d)
126

Description

### Problem description

A Trust Chain search inside a dry-run policy always fails, and the UI reports it as a generic **"An unexpected error occurred."** with no indication of the cause.

The failure is deterministic, not intermittent. `messagesReportBlock` builds its report through `MessagesReport`, which force-loads the document body for several message types:

```ts
// policy-service/src/policy-engine/helpers/messages-report.ts:133
if (this.needDocument(message)) { // VCDocument | VPDocument | RoleDocument
await MessageServer.loadDocument(message, null, { dryRun: ref.dryRun, mockId: ref.mockId });
}
```

`loadDocument` -> `MessageServer.getFile` -> `IPFS.getFile` -> worker `GET_FILE`, which throws unconditionally when the task is a dry run and no mock is configured:

```ts
// worker-service/src/api/worker.ts:419
} else if (task.dryRun) {
throw new Error('Unable to get virtual file');
```

It cannot succeed, because in dry run `ADD_FILE` never stored anything — it returns a random UUID as the "CID":

```ts
// worker-service/src/api/worker.ts:357
} else if (task.dryRun) {
cid = GenerateUUIDv4();
```

So every dry-run document has a CID that was never written anywhere, and any later read of it throws. There is no database fallback on that path.

There are two call sites that abort the report: line 133 (VC / VP / Role messages) and line 246 (`checkSchemas`, which runs for every topic the walk touches). Because `checkMessage` -> `checkTopic` -> `checkSchemas`, a search fails even before it reaches a credential. A third call site at line 282 (`checkUsers`, DID documents) is inside `try { ... } catch { continue; }` and is harmless.

`messagesReportBlock` swallows the reason and reports only a status:

```ts
// policy-service/src/policy-engine/blocks/messages-report-block.ts:78
await ref.setShortCache(this.USER_REPORT_STATUS, 'FAILED', user);
ref.error(`Create Report: ${PolicyUtils.getErrorMessage(error)}`);
```

and the frontend renders that status as a fixed string with no detail:

```html

@if (status === 'FAILED') {

An unexpected error occurred.

Clear
```

The real reason only ever reaches the server log, so from the user's side this looks like a random product failure rather than an unsupported combination.

**There is an escape hatch, but it is off by default.** The "Move to Dry-Run" dialog offers *Enable Mock Data* ("intercept all external calls (IPFS, Topics, Tokens, API) and return pre-recorded responses"). It defaults to `enableMock = false` (`dry-run-dialog.component.ts:19`), and `DryRunSettings.skipMockPrompt(policyId)` returns `{ enableMock: false }` for anyone who ticked "don't ask again", so they are never prompted again. With mocks on, `GET_FILE` is served by `MockService` and the report works.

Enabling Mock Data *after* the dry run has started does not help: the documents were written before any mock existed, so there is nothing recorded to return. With `mockId` set, a mock miss falls through to real IPFS with a UUID in place of a CID, which fails a different way rather than working.

### Step to reproduce

1. Take any policy with a `messagesReportBlock` (Trust Chain) — e.g. one where a Standard Registry can look up a minted VP.
2. Move it to Dry-Run and leave **Enable Mock Data unchecked** (the default).
3. Run the flow far enough to produce a document (a VC, a VP, or a minted token).
4. Open the Trust Chain block and search for that document's HASH / Message ID.
5. The block shows "Please wait while your report is calculated", then switches to **"An unexpected error occurred."** with a Clear button.
6. The server log shows the real cause:

```
[GUARDIAN_SERVICE,,messagesReportBlock,,]: Create Report: Unable to get virtual file
[WORKER.,WORKER]: Task error: , Unable to get virtual file
```

### Expected behavior

A Trust Chain search in a dry-run policy should build the report from the virtual documents the dry run already holds, rather than attempting an IPFS read that cannot ever resolve.

Failing that, the block should at least say what happened — "Trust Chain is not available in dry-run without Mock Data" is actionable, "An unexpected error occurred" is not.

### Screenshots

Not attached — the UI shows only the literal text quoted above, with a Clear button.

### Additional context

Seen in production on a multi-tenant deployment: 3 separate incidents across two policies, every one of them a Trust Chain block in a non-mock dry run, each producing exactly the two log lines above. Users reported it as "the trust chain is broken", with nothing pointing at dry run or IPFS.

Two directions for a fix, and I am happy to open a PR for whichever the team prefers:

1. **Make it work** — resolve documents from the dry-run store when `ref.dryRun` is set, instead of routing through `GET_FILE`. Dry run already persists the message payloads, so the data exists.
2. **Make it honest** — keep the current limitation but surface the cause through the block status so the UI can tell the user that Trust Chain needs Mock Data in dry run, and consider defaulting Mock Data on for policies that contain a `messagesReportBlock`.

Verified present on `develop` at `77b4bd283`.

Contributor guide

Open the contributing guide

Research direction

Start with policy-service/src/policy-engine/helpers/messages-report.ts and worker-service/src/api/worker.ts, then trace messages-report-blocks and the frontend messages-report-block component. Reproduce a non-mock dry run and inspect the affected document and schema paths. Done means Trust Chain no longer fails on the missing virtual file, or reports an explicit actionable limitation when Mock Data is required, with coverage for the affected paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.