paperclipai/paperclip · error

Durable PRP run attachment template is invalid.

Error message

Durable PRP run attachment template is invalid.

What it means

persistRunAttachTemplate() requires its argument to be an object with a truthy `provider` field (checked via isRecord(runAttachTemplate.provider)). The template is the provider preparation payload reused for run.attach after restart, so a template without a valid provider record cannot be persisted and the call fails immediately.

Source

Thrown at packages/paperclip-runner/src/control-plane/durable-prp-control-plane.ts:1713

      runAttachTemplate:
        runAttachTemplate === undefined
          ? null
          : structuredClone(runAttachTemplate),
    });
    this.#identity = structuredClone(identity);
    this.#store.save();
  }

  /**
   * Retain the connection-free provider preparation payload before the first
   * runner bootstrap. Completed command history is bounded and may be
   * compacted before a warm continuation arrives, so it cannot be the sole
   * source for a later run.attach. Repeating the same write is idempotent;
   * changing an established seed fails closed.
   */
  persistRunAttachTemplate(runAttachTemplate: Record<string, unknown>): void {
    if (!isRecord(runAttachTemplate.provider)) {
      throw new Error("Durable PRP run attachment template is invalid.");
    }
    const existing = this.#store.state.runAttachTemplate;
    if (
      existing !== undefined &&
      existing !== null &&
      canonicalJson(existing) !== canonicalJson(runAttachTemplate)
    ) {
      throw new Error("Durable PRP run attachment template conflicts.");
    }
    if (existing !== undefined && existing !== null) return;
    this.#store.state.runAttachTemplate = structuredClone(runAttachTemplate);
    this.#store.save();
  }

  issueBootstrapTicket(ttlMs = 5_000): string {
    this.#store.assertWritable();
    if (this.#store.state.warmTransition) {
      throw new Error(

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the template includes a valid provider object, e.g. { provider: { ... }, ... }, before calling persistRunAttachTemplate.
  2. Validate the template shape at construction time (check typeof template.provider === "object" && template.provider !== null).
  3. Trace where the template is built; fix the config/source that omitted the provider section.
  4. If the template legitimately has no provider, this API is the wrong entry point — review the intended flow.

Example fix

// before
controlPlane.persistRunAttachTemplate({ model: "opus" }); // no provider
// after
controlPlane.persistRunAttachTemplate({ provider: { name: "anthropic" }, model: "opus" });
Defensive patterns

Strategy: validation

Validate before calling

function isValidAttachTemplate(t: unknown): t is Record<string, unknown> & { provider: Record<string, unknown> } {
  return typeof t === "object" && t !== null && typeof (t as any).provider === "object" && (t as any).provider !== null;
}
if (!isValidAttachTemplate(template)) throw new Error("attach template requires a provider object");

Type guard

function hasProvider(t: unknown): t is { provider: Record<string, unknown> } {
  return typeof t === "object" && t !== null && "provider" in t && typeof (t as { provider: unknown }).provider === "object" && (t as { provider: unknown }).provider !== null;
}

Try / catch

try {
  controlPlane.persistRunAttachTemplate(template);
} catch (err) {
  if (err instanceof Error && err.message.includes("run attachment template is invalid")) {
    throw new Error(`attach template missing provider: ${JSON.stringify(template)}`);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling persistRunAttachTemplate() with null/undefined coerced in, an object missing the provider key, provider set to a non-object (string, number, boolean), or provider explicitly null.

Common situations: Config loading produced a partial template (provider section absent); a caller passes the wrong variable; JSON round-trip dropped or renamed the provider field; template built dynamically with an optional provider that was skipped.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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