gastownhall/beads · error

loading attachment %s: %w

Error message

loading attachment %s: %w

What it means

After the --attach argument resolves to an ID, bd mol pour loads that attachment issue with GetIssue before validating it. This error wraps a failed load, preserving the cause. The ID resolved, but the issue could not be fetched — storage failure, concurrent deletion, or the attachment being a wisp invisible to GetIssue.

Source

Thrown at cmd/bd/mol_proxied_server.go:89

			if err != nil {
				return pourProxiedResult{}, "", fmt.Errorf("loading proto: %w", err)
			}
		}

		type attachmentInfo struct {
			id       string
			issue    *types.Issue
			subgraph *TemplateSubgraph
		}
		var attachments []attachmentInfo
		for _, attachArg := range in.attachArgs {
			attachID, err := utils.ResolvePartialID(ctx, w, attachArg)
			if err != nil {
				return pourProxiedResult{}, "", fmt.Errorf("resolving attachment ID %s: %w", attachArg, err)
			}
			attachIssue, err := w.GetIssue(ctx, attachID)
			if err != nil {
				return pourProxiedResult{}, "", fmt.Errorf("loading attachment %s: %w", attachID, err)
			}
			if !isProto(attachIssue) {
				return pourProxiedResult{}, "", fmt.Errorf("%s is not a proto (missing '%s' label)", attachID, MoleculeLabel)
			}
			attachSubgraph, err := loadTemplateSubgraph(ctx, w, attachID)
			if err != nil {
				return pourProxiedResult{}, "", fmt.Errorf("loading attachment subgraph %s: %w", attachID, err)
			}
			attachments = append(attachments, attachmentInfo{attachID, attachIssue, attachSubgraph})
		}

		vars := applyVariableDefaults(vars, subgraph)

		var attachSubgraphs []*TemplateSubgraph
		for _, a := range attachments {
			attachSubgraphs = append(attachSubgraphs, a.subgraph)
		}
		if err := checkPourVars(subgraph, attachSubgraphs, vars); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the command — transient races/locks often resolve on retry
  2. Confirm the attachment exists: bd show <attachID>
  3. Ensure the attachment is a persistent (non-wisp) issue
  4. Check for and wait out concurrent bd processes holding the DB lock

Example fix

// before
attachIssue, err := w.GetIssue(ctx, attachID)
if err != nil { return fmt.Errorf("loading attachment %s: %w", attachID, err) }
// after: distinguish not-found from transient
attachIssue, err := w.GetIssue(ctx, attachID)
if err != nil {
	if errors.Is(err, sql.ErrNoRows) { return fmt.Errorf("attachment %s no longer exists", attachID) }
	return fmt.Errorf("loading attachment %s: %w", attachID, err)
}
Defensive patterns

Strategy: retry

Validate before calling

// verify each attachment is loadable before pouring
for _, id := range attachIDs {
	if _, err := reader.GetIssue(ctx, id); err != nil {
		return fmt.Errorf("attachment %s not loadable: %w", id, err)
	}
}

Try / catch

err := pourCmd(args)
if err != nil && strings.Contains(err.Error(), "loading attachment ") {
	time.Sleep(time.Second)
	err = pourCmd(args) // retry transient storage failures once
}

Prevention

When it happens

Trigger: Running `bd mol pour <proto> --attach <id>` where GetIssue fails after successful ID resolution: concurrent deletion of the attachment, Dolt lock/corruption, context timeout, or ephemeral wisp attachments not loadable via the normal issue path.

Common situations: Another agent removed the attachment between resolve and load; database locked by concurrent bd commands; attaching an ephemeral issue that gc'ed mid-operation.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/f4f5c338f90af921. Report an issue: GitHub.