Stirling-Tools/Stirling-PDF · error · Error
Missing stored file ID in response.
Error message
Missing stored file ID in response.
What it means
Thrown by uploadHistoryChain when POST /api/v1/storage/files returned without throwing (axios 2xx) but response.data.id is falsy. The backend storage endpoint did not return a numeric stored-file identifier.
Source
Thrown at frontend/editor/src/core/services/serverStorageUpload.ts:66
);
const formData = new FormData();
formData.append("file", finalFile, finalFile.name);
formData.append("historyBundle", bundleFile, bundleFile.name);
formData.append("auditLog", auditLog, auditLog.name);
if (existingRemoteId) {
const response = await apiClient.put(
`/api/v1/storage/files/${existingRemoteId}`,
formData,
);
const updatedAt = resolveUpdatedAt(response.data?.updatedAt);
return { remoteId: existingRemoteId, updatedAt, chain };
}
const response = await apiClient.post("/api/v1/storage/files", formData);
const remoteId = response.data?.id as number | undefined;
if (!remoteId) {
throw new Error("Missing stored file ID in response.");
}
const updatedAt = resolveUpdatedAt(response.data?.updatedAt);
return { remoteId, updatedAt, chain };
}
export async function uploadHistoryChains(
originalFileIds: FileId[],
existingRemoteId?: number,
): Promise<{ remoteId: number; updatedAt: number; chain: StirlingFileStub[] }> {
const uniqueRoots = Array.from(new Set(originalFileIds));
const chainMap = new Map<FileId, StirlingFileStub[]>();
const combinedChain: StirlingFileStub[] = [];
const seenIds = new Set<FileId>();
const leafStubs: StirlingFileStub[] = [];
for (const rootId of uniqueRoots) {
const chain = await fileStorage.getHistoryChainStubs(rootId);View on GitHub (pinned to 9ef20dcab8)
Solutions
- Confirm the backend POST /api/v1/storage/files returns { id: number } at response.data.id.
- Inspect the actual response body in the browser network tab.
- Align frontend and backend versions so the contract matches.
- Ensure the request was not silently redirected to an auth/login route.
Example fix
// before
const remoteId = response.data?.id as number | undefined;
if (!remoteId) throw new Error('Missing stored file ID in response.');
// after
const remoteId = response.data?.id ?? response.data?.file?.id;
if (typeof remoteId !== 'number' || !Number.isFinite(remoteId)) {
console.error('Unexpected storage response:', response.data);
throw new Error('Storage endpoint did not return a file id.');
} Defensive patterns
Strategy: try-catch
Try / catch
try {
const res = await apiClient.post('/api/v1/storage/files', formData);
const id = res.data?.id;
if (typeof id !== 'number') {
throw new Error(`Storage response missing numeric id: ${JSON.stringify(res.data).slice(0, 200)}`);
}
return { remoteId: id, updatedAt: resolveUpdatedAt(res.data?.updatedAt) };
} catch (e) {
// distinguish network error from contract error for the user
throw e instanceof Error ? e : new Error('Upload failed');
} Prevention
- Pin frontend and backend to matching versions; the /api/v1/storage/files id contract is load-bearing.
- Log the raw response body when id is missing so contract drift is diagnosable.
- Watch for auth redirects that return HTML with a 2xx status.
When it happens
Trigger: The backend responded 2xx but the body lacks an id field, or the field was renamed/moved; an interceptor reshaped the payload; or a 2xx auth/HTML body was captured.
Common situations: Frontend/backend version mismatch where the endpoint contract changed; Spring Security returned a login page captured as 200; a proxy/gateway rewrote the envelope; response wrapped under data.file.id.
Related errors
- ${operationName} operation failed: ${error.response?.data ||
- OCR service error: ${title}
- Response is not a valid PDF. Header: "${head}"
- IndexedDB context not available
- Database not initialized
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/3acd708726523741.
Report an issue: GitHub.