Skip to content

Commit 7be8309

Browse files
authored
fix: improve error handling when packfile cannot be read (fixes #1163) (#2295)
* fix: improve error handling when packfile cannot be read (fixes #1163) FileSystem.read() silently returns null on errors (including ERR_FS_FILE_TOO_LARGE). In GitPackIndex.readSlice(), the null check tested the Promise object (always truthy) instead of the resolved value, causing a confusing TypeError on .slice(null). - Await the promise before null-checking in readSlice() - Add null check in readObjectPacked after resolving pack data - Clear cached promise on failure to allow retry on transient errors - Add test verifying descriptive InternalError on missing packfile * fix(ci): correct test script name in CI workflow The CI workflow referenced `test.jest` which does not exist in package-scripts.cjs. The correct script name is `test.node`.
1 parent b55bd5f commit 7be8309

4 files changed

Lines changed: 50 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
npx nps \
5555
test.typecheck \
5656
test.setup \
57-
test.jest
57+
test.node
5858
5959
- name: Save test results
6060
if: ${{ !cancelled() }}

__tests__/test-packfileIntegrity.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,41 @@ describe('packfile integrity', () => {
140140
expect(obj2).not.toBeNull()
141141
expect(obj2.format).toBe(obj1.format)
142142
})
143+
144+
it('should throw descriptive error when packfile cannot be read', async () => {
145+
// Setup - use a repo with packfile
146+
const { fs, dir } = await makeFixture('test-readObject.git')
147+
const cache = {}
148+
const gitdir = dir
149+
150+
// Use a known OID from the packfile
151+
const oid = '0001c3e2753b03648b6c43dd74ba7fe2f21123d6'
152+
153+
// Delete the .pack file but keep the .idx file
154+
// This causes fs.read() to return null (file not found)
155+
const packDir = gitdir + '/objects/pack'
156+
const files = await fs.readdir(packDir)
157+
const packFile = files.find(f => f.endsWith('.pack'))
158+
if (!packFile) {
159+
throw new Error('No packfile found in ' + packDir)
160+
}
161+
await fs.rm(packDir + '/' + packFile)
162+
163+
// Test - should throw InternalError with descriptive message
164+
let error = null
165+
try {
166+
await readObjectPacked({
167+
fs,
168+
cache,
169+
gitdir,
170+
oid,
171+
})
172+
} catch (e) {
173+
error = e
174+
}
175+
176+
expect(error).not.toBeNull()
177+
expect(error instanceof Errors.InternalError).toBe(true)
178+
expect(error.data.message).toContain('Could not read packfile')
179+
})
143180
})

src/models/GitPackIndex.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,13 @@ export class GitPackIndex {
289289
0b1100000: 'ofs_delta',
290290
0b1110000: 'ref_delta',
291291
}
292-
if (!this.pack) {
292+
const pack = await this.pack
293+
if (!pack) {
293294
throw new InternalError(
294-
'Tried to read from a GitPackIndex with no packfile loaded into memory'
295+
'Could not read packfile data. The packfile may be missing, corrupted, or too large to read into memory.'
295296
)
296297
}
297-
const raw = (await this.pack).slice(start)
298+
const raw = pack.slice(start)
298299
const reader = new BufferCursor(raw)
299300
const byte = reader.readUInt8()
300301
// Object type is encoded in bits 654

src/storage/readObjectPacked.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ export async function readObjectPacked({
2626
if (p.error) throw new InternalError(p.error)
2727
// If the packfile DOES have the oid we're looking for...
2828
if (p.offsets.has(oid)) {
29-
// Get the resolved git object from the packfile
29+
// Derive the .pack path from the .idx path
30+
const packFile = indexFile.replace(/idx$/, 'pack')
3031
if (!p.pack) {
31-
const packFile = indexFile.replace(/idx$/, 'pack')
3232
p.pack = fs.read(packFile)
3333
}
3434
const pack = await p.pack
35+
if (!pack) {
36+
p.pack = null
37+
throw new InternalError(
38+
`Could not read packfile at ${packFile}. The file may be missing, corrupted, or too large to read into memory.`
39+
)
40+
}
3541

3642
// === Packfile Integrity Verification ===
3743
// Performance optimization: use _checksumVerified flag to verify only once per packfile

0 commit comments

Comments
 (0)