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

  1. Check the Bot Framework/teams adapter version for response-shape changes; pin a version where app.send returns the sent message resource.
  2. Log the raw result of app.send at this call site to see the actual shape returned.
  3. If using a mocked adapter in tests, make the mock return { id: '<messageId>' }.
  4. 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

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/42d5bc8fff840c1b. Report an issue: GitHub.