laurent22/joplin · error · Error
Delta link missing: ${JSON.stringify(response)}
Error message
Delta link missing: ${JSON.stringify(response)} What it means
Thrown by the OneDrive driver's delta pagination logic. When the delta response has no @odata.nextLink (more pages coming) it expects an @odata.deltaLink (feed complete, use this token next time). If neither is present the response shape is unexpected and the driver refuses to continue, since proceeding would lose the delta cursor.
Source
Thrown at packages/lib/file-api-driver-onedrive.ts:356
// At OneDrive for Business delta requests can only make at the root of OneDrive. Not sure but it's possible that
// the delta API also returns events for files that are copied outside of the app directory and later deleted or
// modified when using OneDrive Personal).
for (let i = 0; i < response.value.length; i++) {
const v = response.value[i];
if (v.parentReference.id !== pathId) continue;
items.push(this.makeItem_(v));
}
output.items = output.items.concat(items);
let nextLink = null;
if (response['@odata.nextLink']) {
nextLink = response['@odata.nextLink'];
output.hasMore = true;
} else {
if (!response['@odata.deltaLink']) throw new Error(`Delta link missing: ${JSON.stringify(response)}`);
nextLink = response['@odata.deltaLink'];
}
output.context = { nextLink: nextLink };
// https://dev.onedrive.com/items/view_delta.htm
// The same item may appear more than once in a delta feed, for various reasons. You should use the last occurrence you see.
// So remove any duplicate item from the array.
const temp: ItemStat[] = [];
const seenPaths = [];
for (let i = output.items.length - 1; i >= 0; i--) {
const item = output.items[i];
if (seenPaths.indexOf(item.path) >= 0) continue;
temp.splice(0, 0, item);
seenPaths.push(item.path);
}
output.items = temp;View on GitHub (pinned to 2654b33620)
Solutions
- Re-authenticate the OneDrive account (re-link the sync target) — expired tokens often produce malformed responses.
- Inspect the JSON in the message to see what OneDrive actually returned (error body, throttling JSON, etc.).
- Retry after a short delay — transient Graph API anomalies frequently self-correct.
- Confirm the app requests the Files.ReadWrite.All scope and the endpoint URL is the standard Graph delta URL.
Example fix
// before
if (!response['@odata.deltaLink']) throw new Error(`Delta link missing: ${JSON.stringify(response)}`);
// after - tolerate throttling/error envelopes and retry
if (!response['@odata.deltaLink'] && !response.error) {
throw new Error(`Delta link missing: ${JSON.stringify(response)}`);
}
if (response.error) { /* re-auth or back off and retry */ } Defensive patterns
Strategy: retry
Validate before calling
// Re-check token validity and endpoint shape before delta.
if (!tokenValid()) await reauthenticate();
const probe = await fetch(deltaUrl, { headers });
if (!probe.ok || probe.headers.get('content-type')?.includes('text/html')) {
throw new Error('OneDrive delta endpoint returned a non-JSON response; check auth/URL.');
} Type guard
function hasDeltaOrNextLink(response: any): boolean {
return !!response && (!!response['@odata.deltaLink'] || !!response['@odata.nextLink']);
} Try / catch
for (const delay of [0, 1000, 5000]) {
await sleep(delay);
try {
const response = await graphDelta();
if (hasDeltaOrNextLink(response)) { /* proceed */ break; }
if (response?.error) throw new Error('OneDrive error: ' + JSON.stringify(response.error));
} catch (e) { if (delay === 5000) throw e; }
} Prevention
- Refresh the OneDrive token proactively before it expires.
- Confirm the delta URL is the standard Graph endpoint.
- Treat throttling (429) responses separately from delta envelopes.
- Log raw responses during sync so non-standard shapes are diagnosable.
When it happens
Trigger: Calling delta() on OneDrive where the Graph API response omits both @odata.nextLink and @odata.deltaLink — e.g. malformed/empty response, API version returning a different shape, token expiry causing a truncated response.
Common situations: OneDrive Graph API version drift; expired/de auth token producing a non-delta body; throttling response (Retry-After) mis-parsed as a delta; corporate SharePoint endpoint with non-standard behavior; rare backend hiccup returning an empty envelope.
Related errors
- uploadBlob: ${method} ${url}: ${error.toString()}
- AWS S3 bucket not found: ${SyncTargetAmazonS3.s3BucketName()
- Could not access data on server "${options.path()}"
- User is not authenticated
- WebDAV directory not found: ${options.path()}
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/76d6a9373e4dfd0a.
Report an issue: GitHub.