github/copilot-sdk · error

step(" ") journal returned a hit without a result

Error message

step("${key}") journal returned a hit without a result

What it means

Thrown when the step journal (cache) reports a cache hit for a factory step but stores no serialized result (`cached.resultJson === undefined`). This is an internal-invariant violation: a journal hit must always carry the recorded result. The SDK raises rather than returning undefined data.

Solutions

  1. Clear/reset the journal for this run so the step re-executes fresh
  2. Check journal storage integrity (file size, serialization) and repair or delete corrupt entries
  3. Verify all writers/consumers use the same SDK/journal format version
  4. If persisting results externally, ensure hit markers are only written together with resultJson

Example fix

// before (corrupt journal kept)
await session.factory.execute({ name: "build" }); // step("compile") hits empty journal entry
// after
fs.rmSync(journalPath); // reset journal
await session.factory.execute({ name: "build" });
Defensive patterns

Strategy: fallback

Validate before calling

const entry = journal.get(stepKey);
if (entry?.hit && entry?.resultJson === undefined) journal.delete(stepKey); // drop corrupt hit

Type guard

function isValidJournalHit(e) {
  return e != null && e.hit === true && typeof e.resultJson === "string";
}

Try / catch

try {
  return await runFactoryStep(key, input);
} catch (err) {
  if (String(err?.message).includes('journal returned a hit without a result')) {
    resetJournal(runId);
    return await runFactoryStep(key, input); // rerun with clean journal
  }
  throw err;
}

Prevention

When it happens

Trigger: Journal/step cache entry marked as hit with missing resultJson; corrupted or hand-edited journal storage; a journal writer version writing hits without results; resume of a run whose journal entry was truncated.

Common situations: Resuming interrupted factory runs with a journal file that was partially written or corrupted; switching SDK versions with an incompatible journal format; manually pruning result payloads from the journal while keeping hit markers.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/a7891562749c9ede. Report an issue: GitHub.

Appendix: source

Thrown at nodejs/src/session.ts:1540

                                // between entering step() and running the producer. The
                                // journaled branch is covered by awaitFactoryOperation;
                                // this one has to check for itself, or a cancelled run
                                // would still start new extension work.
                                throwIfFactoryAborted(controller.signal);
                                return producer();
                            }
                            const cached = await awaitFactoryOperation(
                                () =>
                                    self.rpc.factory.journal.get({
                                        runId: params.runId,
                                        executionToken: params.executionToken,
                                        key,
                                    }),
                                controller.signal
                            );
                            if (cached.hit) {
                                if (cached.resultJson === undefined) {
                                    throw new Error(
                                        `step("${key}") journal returned a hit without a result`
                                    );
                                }
                                assertFactoryStepResult(cached.resultJson, key);
                                return cached.resultJson;
                            }

                            // Producers are best-effort at-least-once across crashes or
                            // concurrent callers, so authors must make side effects idempotent.
                            const result = await producer();
                            assertFactoryStepResult(result, key);
                            await awaitFactoryOperation(
                                () =>
                                    self.rpc.factory.journal.put({
                                        runId: params.runId,
                                        executionToken: params.executionToken,
                                        key,
                                        resultJson: result,

View on GitHub (pinned to cd8cf15dc3)