{"record":{"id":"7ab17fd8e804c878","repo":"gastownhall/beads","slug":"workspacegate-probe-s-w","errorCode":null,"errorMessage":"workspacegate: probe %s: %w","messagePattern":"workspacegate: probe (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/workspacegate/gate.go","lineNumber":584,"sourceCode":"// spurious failures into concurrent fail-fast acquirers.\n//\n// The error return is non-nil when the state could not be determined\n// (unreadable gate file, unsupported filesystem); callers must not treat\n// that as \"not held\".\nfunc (g Gate) ExclusiveHolder() (held bool, info *Info, err error) {\n\tif g.path == \"\" {\n\t\treturn false, nil, errors.New(\"workspacegate: zero Gate\")\n\t}\n\t// O_RDONLY: this is a read-only probe (flock does not require a\n\t// writable descriptor), and it widens reach — a gate file owned by\n\t// another user with no write permission for us is still probeable.\n\tf, err := os.OpenFile(g.path, os.O_RDONLY, 0o600)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\t// No gate file: nothing has ever gated here.\n\t\t\treturn false, nil, nil\n\t\t}\n\t\treturn false, nil, fmt.Errorf(\"workspacegate: probe %s: %w\", g.path, err)\n\t}\n\tdefer f.Close()\n\n\tif lerr := lockfile.FlockSharedNonBlock(f); lerr == nil {\n\t\t_ = lockfile.FlockUnlock(f)\n\t\treturn false, nil, nil\n\t} else if !errors.Is(lerr, lockfile.ErrLockBusy) && !lockfile.IsLocked(lerr) {\n\t\treturn false, nil, fmt.Errorf(\"workspacegate: probe %s: %w\", g.path, lerr)\n\t}\n\treturn true, g.readInfo(), nil\n}\n","sourceCodeStart":566,"sourceCodeEnd":596,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/workspacegate/gate.go#L566-L596","documentation":"ExclusiveHolder probed the gate by opening the file read-only; the open failed with an error other than NotExist (which is treated as 'never gated'). The os.PathError is wrapped so callers can inspect it. This happens before any flock test, so no holder information is returned.","triggerScenarios":"Calling ExclusiveHolder when os.OpenFile(path, O_RDONLY) fails with e.g. EACCES (no read permission on an existing 0o600 gate file owned by another user), EISDIR (path is a directory), or ELOOP (symlink cycle).","commonSituations":"Probe run under a different uid than the Acquire process (0o600 file); the gate path was repointed at a directory; permissions changed after an earlier run created the gate.","solutions":["Run the probe as the same user that created the gate, or chmod the gate file to be group/world readable if cross-user probing is intended.","Verify the path is a regular file (os.Stat, mode check) and not a directory or broken symlink.","Handle os.IsNotExist as the normal 'nothing gated' case and only treat other errors as failures.","Fix path configuration if the gate path was recently moved or renamed."],"exampleFix":"// before\nheld, _, err := g.ExclusiveHolder(ctx)\n\n// after\nif fi, serr := os.Stat(g.Path()); serr == nil && fi.IsDir() {\n    return fmt.Errorf(\"gate path is a directory: %s\", g.Path())\n}\nheld, _, err := g.ExclusiveHolder(ctx)\nif err != nil {\n    var pe *os.PathError\n    if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrPermission) {\n        return fmt.Errorf(\"gate unreadable by current user: %w\", err)\n    }\n}","handlingStrategy":"validation","validationCode":"fi, err := os.Stat(gatePath)\nif os.IsNotExist(err) {\n    // nothing has ever gated here — safe to proceed\n} else if err != nil {\n    return err\n} else if !fi.Mode().IsRegular() {\n    return fmt.Errorf(\"gate path is not a regular file: %s\", gatePath)\n}","typeGuard":null,"tryCatchPattern":"held, info, err := g.ExclusiveHolder(ctx)\nif err != nil {\n    var pe *os.PathError\n    if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrPermission) {\n        // run as same user as gate creator or fix permissions\n        return fmt.Errorf(\"gate unreadable: %w\", err)\n    }\n    return err\n}","preventionTips":["Probe as the same user that created the gate (0o600 default).","Treat os.IsNotExist as the normal 'no gate' case before reporting failure.","Verify the path is a regular file, not a directory or symlink loop."],"tags":["filesystem","file-permissions","locking","workspacegate"],"backgroundTag":"file-open-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}