{"record":{"id":"a7891562749c9ede","repo":"github/copilot-sdk","slug":"step-key-journal-returned-a-hit-without-a-re","errorCode":null,"errorMessage":"step(\"${key}\") journal returned a hit without a result","messagePattern":"step\\(\"(.+?)\"\\) journal returned a hit without a result","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/session.ts","lineNumber":1540,"sourceCode":"                                // between entering step() and running the producer. The\n                                // journaled branch is covered by awaitFactoryOperation;\n                                // this one has to check for itself, or a cancelled run\n                                // would still start new extension work.\n                                throwIfFactoryAborted(controller.signal);\n                                return producer();\n                            }\n                            const cached = await awaitFactoryOperation(\n                                () =>\n                                    self.rpc.factory.journal.get({\n                                        runId: params.runId,\n                                        executionToken: params.executionToken,\n                                        key,\n                                    }),\n                                controller.signal\n                            );\n                            if (cached.hit) {\n                                if (cached.resultJson === undefined) {\n                                    throw new Error(\n                                        `step(\"${key}\") journal returned a hit without a result`\n                                    );\n                                }\n                                assertFactoryStepResult(cached.resultJson, key);\n                                return cached.resultJson;\n                            }\n\n                            // Producers are best-effort at-least-once across crashes or\n                            // concurrent callers, so authors must make side effects idempotent.\n                            const result = await producer();\n                            assertFactoryStepResult(result, key);\n                            await awaitFactoryOperation(\n                                () =>\n                                    self.rpc.factory.journal.put({\n                                        runId: params.runId,\n                                        executionToken: params.executionToken,\n                                        key,\n                                        resultJson: result,","sourceCodeStart":1522,"sourceCodeEnd":1558,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/session.ts#L1522-L1558","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (corrupt journal kept)\nawait session.factory.execute({ name: \"build\" }); // step(\"compile\") hits empty journal entry\n// after\nfs.rmSync(journalPath); // reset journal\nawait session.factory.execute({ name: \"build\" });","handlingStrategy":"fallback","validationCode":"const entry = journal.get(stepKey);\nif (entry?.hit && entry?.resultJson === undefined) journal.delete(stepKey); // drop corrupt hit","typeGuard":"function isValidJournalHit(e) {\n  return e != null && e.hit === true && typeof e.resultJson === \"string\";\n}","tryCatchPattern":"try {\n  return await runFactoryStep(key, input);\n} catch (err) {\n  if (String(err?.message).includes('journal returned a hit without a result')) {\n    resetJournal(runId);\n    return await runFactoryStep(key, input); // rerun with clean journal\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["journal","cache","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}