sipeed/picoclaw · error

error adding job: %w

Error message

error adding job: %w

What it means

After building the CronSchedule, `picoclaw cron add` calls cron.NewCronService(storePath(), nil).AddJob(...). AddJob appends the job in memory and then persists the whole store with saveStoreUnsafe. This error wraps that persistence (or store-load) failure, so the job was NOT durably added even though the ID logic ran.

Source

Thrown at cmd/picoclaw/internal/cron/add.go:41

		Short: "Add a new scheduled job",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, _ []string) error {
			if every <= 0 && cronExp == "" {
				return fmt.Errorf("either --every or --cron must be specified")
			}

			var schedule cron.CronSchedule
			if every > 0 {
				everyMS := every * 1000
				schedule = cron.CronSchedule{Kind: "every", EveryMS: &everyMS}
			} else {
				schedule = cron.CronSchedule{Kind: "cron", Expr: cronExp}
			}

			cs := cron.NewCronService(storePath(), nil)
			job, err := cs.AddJob(name, schedule, message, channel, to)
			if err != nil {
				return fmt.Errorf("error adding job: %w", err)
			}

			fmt.Printf("✓ Added job '%s' (%s)\n", job.Name, job.ID)

			return nil
		},
	}

	cmd.Flags().StringVarP(&name, "name", "n", "", "Job name")
	cmd.Flags().StringVarP(&message, "message", "m", "", "Message for agent")
	cmd.Flags().Int64VarP(&every, "every", "e", 0, "Run every N seconds")
	cmd.Flags().StringVarP(&cronExp, "cron", "c", "", "Cron expression (e.g. '0 9 * * *')")
	cmd.Flags().StringVar(&to, "to", "", "Recipient for delivery")
	cmd.Flags().StringVar(&channel, "channel", "", "Channel for delivery")

	_ = cmd.MarkFlagRequired("name")
	_ = cmd.MarkFlagRequired("message")
	cmd.MarkFlagsMutuallyExclusive("every", "cron")

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Read the wrapped error: a permission string means fixing ownership of <workspace>/cron/jobs.json; ENOENT means creating the directory
  2. Ensure the workspace path exists and is writable: `mkdir -p <workspace>/cron`
  3. If jobs.json is corrupt, move it aside (it is recreated empty) and re-add your jobs
  4. Re-run `picoclaw cron add ...` and confirm with `picoclaw cron list`

Example fix

# before: cron dir missing, add fails
picoclaw cron add -n ping -m hi --every 60
# after
mkdir -p "$PICOCLOW_WORKSPACE/cron"
picoclaw cron add -n ping -m hi --every 60
Defensive patterns

Strategy: try-catch

Validate before calling

ws=$(picoclaw config path 2>/dev/null) # or read configured workspace
mkdir -p "$ws/cron" && [ -w "$ws/cron" ] && echo ok || echo "cron store dir missing/unwritable"

Try / catch

if _, err := cs.AddJob(name, schedule, message, channel, to); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) {
        // jobs.json or its dir: create dir / fix ownership, then re-add
    }
    return fmt.Errorf("add job: %w", err)
}

Prevention

When it happens

Trigger: The file <workspace>/cron/jobs.json (path resolved by the cron command's PersistentPreRunE from the config) cannot be created, read, or written: missing cron directory, wrong ownership, disk full, or existing jobs.json is invalid JSON so the initial store load fails.

Common situations: Workspace on a read-only or dropped mount; permissions changed after initial setup; jobs.json truncated by a crash; running the CLI as a different user than the workspace owner.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/1f337b1408834d6c. Report an issue: GitHub.