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
- Clear/reset the journal for this run so the step re-executes fresh
- Check journal storage integrity (file size, serialization) and repair or delete corrupt entries
- Verify all writers/consumers use the same SDK/journal format version
- 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
- Never hand-edit or partially prune journal files
- Use the same SDK version for writing and reading a journal
- Persist hit markers and resultJson atomically
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
- Concurrent extraction race: target already exists but is…
- Published runtime wrapper is not a non-empty executable…
- Incomplete Copilot runtime bundle in
- Copilot runtime bundle is not cached in
- failed to hash existing output
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)