sipeed/picoclaw · error

write result: %w

Error message

write result: %w

What it means

os.WriteFile failure while persisting an individual eval result file eval_<mode>_<sampleID>.json under outDir. The error wraps the underlying syscall (permission, ENOSPC, ENAMETOOLONG, ENOENT if the directory vanished mid-run). Because the loop aborts on first failure, later samples are not written.

Source

Thrown at cmd/membench/eval.go:278

		ByCategory:     byCat,
		TotalQuestions: len(qaResults),
		ValidF1Count:   validF1Count,
	}
}

// SaveResults writes per-sample eval results to JSON files.
func SaveResults(results []EvalResult, outDir string) error {
	if err := os.MkdirAll(outDir, 0o755); err != nil {
		return fmt.Errorf("create output dir: %w", err)
	}
	for _, r := range results {
		path := filepath.Join(outDir, fmt.Sprintf("eval_%s_%s.json", r.Mode, r.SampleID))
		data, err := json.MarshalIndent(r, "", "  ")
		if err != nil {
			return fmt.Errorf("marshal result: %w", err)
		}
		if err := os.WriteFile(path, data, 0o644); err != nil {
			return fmt.Errorf("write result: %w", err)
		}
	}
	return nil
}

// SaveAggregated writes a combined results.json with all modes.
func SaveAggregated(results []EvalResult, outDir string) error {
	byMode := map[string][]EvalResult{}
	for _, r := range results {
		byMode[r.Mode] = append(byMode[r.Mode], r)
	}

	aggMap := map[string]AggMetrics{}
	for mode, modeResults := range byMode {
		aggMap[mode] = computeModeAgg(modeResults)
	}

	data, err := json.MarshalIndent(aggMap, "", "  ")

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Check df/space on the output volume and free room, then rerun (ingestion is idempotent via existing-conversation check)
  2. Sanitize SampleID before it reaches the filename: strip '/' and length-cap it
  3. Verify outDir still exists and is writable immediately before the run
  4. Write to a local directory first and sync results off-box, instead of writing directly to network storage

Example fix

// before
path := filepath.Join(outDir, fmt.Sprintf("eval_%s_%s.json", r.Mode, r.SampleID))

// after
safeID := strings.Map(func(c rune) rune {
    if c == '/' || c == os.PathSeparator { return '_' }
    return c
}, r.SampleID)
path := filepath.Join(outDir, fmt.Sprintf("eval_%s_%s.json", r.Mode, safeID))
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(outDir); err == nil && !fi.IsDir() {
    return fmt.Errorf("%s exists and is a file", outDir)
}
// plus a write-probe as in the mkdir check

Try / catch

var pathErr *fs.PathError
if errors.As(err, &pathErr) {
    switch {
    case errors.Is(pathErr.Err, syscall.ENOSPC):
        log.Printf("disk full writing %s; freeing space", pathErr.Path)
    case errors.Is(pathErr.Err, syscall.ENAMETOOLONG):
        log.Printf("filename too long for sample %s", r.SampleID)
    default:
        log.Printf("write failed: %v", pathErr)
    }
}

Prevention

When it happens

Trigger: Disk fills up mid-run; outDir deleted or unmounted between MkdirAll and WriteFile; SampleID containing '/' or path separators creating an invalid path; filename exceeding NAME_MAX (255) for long sample IDs; SELinux/AppArmor denying writes.

Common situations: Large eval sweeps filling a small tmpfs; container whose volume is rotated mid-run; sample IDs with characters that are legal in the source dataset but illegal in filenames.

Related errors


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