paperclipai/paperclip · error
Teams file-card send receipt is unproven
Error message
Teams file-card send receipt is unproven
What it means
After a successful teams.app.send call, the returned receipt is validated: it must be a record with a non-empty 'id' string of at most 1024 chars containing no control characters. If the provider returns a response that lacks a usable message id, this error is thrown — the send may have succeeded, but no durable proof of delivery exists.
Source
Thrown at server/src/services/chat-sdk-runtime.ts:1273
// ALS as ordinary thread operations. Thread.post would turn these
// provider-native cards into AdaptiveCards.
result = await teams.app!.send!(decoded.conversationId as string, {
type: "message",
attachments: [card],
});
} catch {
// Provider errors may contain private card contexts. No implicit
// retry: an uncertain POST remains caller-owned durable evidence.
throw new Error("Teams file-card send result is unknown");
}
if (
!isRecord(result) ||
typeof result.id !== "string" ||
!result.id ||
result.id.length > 1024 ||
/[\x00-\x20\x7f]/.test(result.id)
) {
throw new Error("Teams file-card send receipt is unproven");
}
return { id: result.id };
},
true,
);
};
}
teams.paperclipRecordThreadServiceUrl = async (
threadId: string,
serviceUrlValue: unknown,
) => {
const decoded = teams.decodeThreadId!(threadId);
if (typeof decoded.conversationId !== "string" || !decoded.conversationId) {
throw new TeamsServiceUrlValidationError(
"Teams destination is missing its conversation identity",
);
}View on GitHub (pinned to 01ad858492)
Solutions
- Check the Bot Framework/teams adapter version for response-shape changes; pin a version where app.send returns the sent message resource.
- Log the raw result of app.send at this call site to see the actual shape returned.
- If using a mocked adapter in tests, make the mock return { id: '<messageId>' }.
- Treat the send as unproven and re-send only after confirming no duplicate card was delivered (the runtime deliberately does not auto-retry).
Example fix
// before
const result = await teams.app.send(conversationId, { type: "message", attachments: [card] });
// test double returning undefined
// after
const result = (await teams.app.send(conversationId, { type: "message", attachments: [card] })) ?? { id: crypto.randomUUID() }; // only if your adapter guarantees delivery without an id
// preferred: fix the adapter/mock to return the real message id Defensive patterns
Strategy: type-guard
Validate before calling
const result = await teams.app.send(conversationId, payload);
const hasReceipt = (r: unknown): r is { id: string } =>
typeof r === "object" && r !== null && typeof (r as any).id === "string" &&
(r as any).id.length > 0 && (r as any).id.length <= 1024 &&
!/[\x00-\x20\x7f]/.test((r as any).id);
if (!hasReceipt(result)) throw new Error("no usable message id in send receipt"); Type guard
function hasMessageReceipt(r: unknown): r is { id: string } {
return typeof r === "object" && r !== null && typeof (r as { id?: unknown }).id === "string" &&
(r as { id: string }).id.length > 0 && (r as { id: string }).id.length <= 1024 &&
!/[\x00-\x20\x7f]/.test((r as { id: string }).id);
} Try / catch
try {
const receipt = await teams.paperclipSendFileCard(threadId, kind, input);
recordDelivery(threadId, receipt.id);
} catch (err) {
logger.error({ threadId }, "send receipt unproven; treat delivery as unconfirmed");
throw err;
} Prevention
- Pin the Bot Framework/adapter version and test the send response shape on upgrades.
- Make test doubles return realistic receipts ({ id: '...' }).
- Log raw app.send results in staging to detect shape drift early.
When it happens
Trigger: teams.app.send resolves but the result is not a record, result.id is missing/not a string/empty, exceeds 1024 chars, or contains control characters.
Common situations: Bot Framework SDK version returning a differently shaped response; adapter mock or test double returning undefined; provider returning 201 with an unexpected body shape; custom middleware transforming the response.
Related errors
- Teams destination is missing its verified service URL
- Teams destination contains an invalid service URL
- Teams destination contains an untrusted service URL
- decodeThreadId is unavailable
- openDM is unavailable
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/42d5bc8fff840c1b.
Report an issue: GitHub.