gastownhall/beads · error

no store is open for this workspace

Error message

no store is open for this workspace

What it means

This error is produced by ClassifyPublicCreateError when a create's dependency/parent/waits-for relationship names a row that does not exist in the database. The storage layer returns either a typed *domain.DependencyEndpointNotFoundError or a missing-foreign-key database error; this classifier rewrites it into a deterministic validation refusal: storage.ErrValidation wrapping the original error and storage.ErrNotFound. It exists so every backend reports a missing target uniformly instead of leaking backend-specific infrastructure errors.

Source

Thrown at cmd/bd/serve.go:695

// returns a claimer that runs the workspace's on_update script for every claim
// it lands. This server documents that hooks do not fire, so the hook layer has
// to come off; the telemetry layer beneath it must not, or every request this
// process serves goes unspanned and untimed. httpapi.Listen refuses a
// hook-firing role rather than trusting this comment, so getting it wrong is a
// startup error rather than a silent subprocess per claim.
//
// The assertion is conditional because a BD_NO_HOOKS=1 workspace has no hook
// layer to peel.
//
// It returns the WHOLE set httpapi.Config requires; Listen refuses a partial
// set (see checkDatabaseSource), so a role missing here is a startup failure
// rather than a nil dereference on the first request that reaches it.
func serveIssueRoles(src serveRoleSource, journalEnabled bool) (serveRoles, error) {
	var roles serveRoles
	if src == nil {
		// A set of nil roles would reach Listen as "no database source" —
		// true, and useless. Name the condition that actually happened.
		return roles, errors.New("no store is open for this workspace")
	}
	if hooked, ok := src.(*storage.HookFiringStore); ok {
		src = hooked.Unwrap()
	}

	// Each entry binds one Config field to the accessor that fills it, and
	// names itself in the failure.
	type binding struct {
		name string
		get  func() error
	}
	for _, b := range []binding{
		{"issue reader", func() (err error) { roles.reader, err = src.IssueReader(); return }},
		{"issue claimer", func() (err error) { roles.claimer, err = src.IssueClaimer(); return }},
		{"batch closer", func() (err error) { roles.batchCloser, err = src.BatchCloser(); return }},
		{"ready claimer", func() (err error) { roles.readyClaimer, err = src.ReadyClaimer(); return }},
		{"issue releaser", func() (err error) { roles.releaser, err = src.Releaser(); return }},
		{"issue lifecycle", func() (err error) { roles.lifecycle, err = src.IssueLifecycle(); return }},

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify each dependency TargetID / ParentID / WaitsFor.SpawnerID exists before creating: look the ID up with the same storage handle.
  2. Fix typos or wrong prefixes in the referenced IDs.
  3. If the target is created in the same batch, ensure ordering/commit so it exists before the referencing create executes.
  4. Handle errors.Is(err, storage.ErrNotFound) in the caller and surface a user-facing 'unknown issue ID' message instead of retrying.

Example fix

// before
req.Dependencies = []publicops.DependencyInput{{TargetID: "bd-999", Type: "blocks"}} // bd-999 not in DB
// after
if _, err := store.GetIssue(ctx, "bd-999"); err != nil { return fmt.Errorf("target bd-999 missing: %w", err) }
req.Dependencies = []publicops.DependencyInput{{TargetID: "bd-999", Type: "blocks"}}
Defensive patterns

Strategy: validation

Validate before calling

for _, d := range req.Dependencies {
    if _, err := store.GetIssue(ctx, d.TargetID); err != nil {
        return fmt.Errorf("dependency target %s does not exist", d.TargetID)
    }
}
if req.ParentID != "" {
    if _, err := store.GetIssue(ctx, req.ParentID); err != nil { return fmt.Errorf("parent %s does not exist", req.ParentID) }
}

Type guard

var missing *domain.DependencyEndpointNotFoundError
if errors.As(err, &missing) || (errors.Is(err, storage.ErrValidation) && errors.Is(err, storage.ErrNotFound)) {
    // missing target
targetID := ""
if errors.As(err, &missing) { targetID = missing.EndpointID }

Try / catch

if err := store.ExecuteCreate(ctx, req); err != nil {
    if errors.Is(err, storage.ErrValidation) && errors.Is(err, storage.ErrNotFound) {
        return fmt.Errorf("unknown dependency target: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ExecuteCreate/ExecuteCreateBatch with a Dependencies entry, ParentID, or WaitsFor.SpawnerID whose TargetID/ID matches no existing issue; the dependency write fails with a missing endpoint or foreign-key violation and ClassifyPublicCreateError rewrites it at public_create.go:115.

Common situations: Typo in a dependency target ID; referencing an issue in a different database/prefix; the target issue was deleted (or was ephemeral and never persisted) before the create ran; copying issue IDs from another environment.

Related errors


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