wagoodman/dive · error
failed to read CI config file %s: %w
Error message
failed to read CI config file %s: %w
What it means
Thrown while loading the CI rules file when os.ReadFile on the user-supplied --ci-config path fails after fileExists() reported it present. The %w preserves the OS error. It only fires when ConfigPath is non-empty and the file exists at check time, so failures are typically transient or permission-related.
Source
Thrown at cmd/dive/cli/internal/options/ci.go:55
func (c *CI) AddFlags(flags clio.FlagSet) {
flags.BoolVarP(&c.Enabled, "ci", "", "skip the interactive TUI and validate against CI rules (same as env var CI=true)")
flags.StringVarP(&c.ConfigPath, "ci-config", "", "if CI=true in the environment, use the given yaml to drive validation rules.")
}
func (c *CI) PostLoad() error {
enabledFromEnv := truthy(os.Getenv("CI"))
if !c.Enabled && enabledFromEnv {
c.Enabled = true
}
if c.ConfigPath != "" {
if fileExists(c.ConfigPath) {
// if a config file is provided, load it and override any values provided in the application config.
// If we're hitting this case we should pretend that only the config file was provided and applied
// on top of the default config values.
yamlFile, err := os.ReadFile(c.ConfigPath)
if err != nil {
return fmt.Errorf("failed to read CI config file %s: %w", c.ConfigPath, err)
}
def := DefaultCIRules()
r := legacyRuleFile{
LowestEfficiencyThresholdString: def.LowestEfficiencyThresholdString,
HighestWastedBytesString: def.HighestWastedBytesString,
HighestUserWastedPercentString: def.HighestUserWastedPercentString,
}
wrapper := struct {
Rules *legacyRuleFile `yaml:"rules"`
}{
Rules: &r,
}
if err := yaml.Unmarshal(yamlFile, &wrapper); err != nil {
return fmt.Errorf("failed to unmarshal CI config file %s: %w", c.ConfigPath, err)
}
// TODO: should this be a deprecated use warning in the future?
c.Rules = CIRules{
LowestEfficiencyThresholdString: r.LowestEfficiencyThresholdString,View on GitHub (pinned to d6c691947f)
Solutions
- Inspect the wrapped error for the exact OS cause ('permission denied', 'no such file or directory').
- Fix permissions: chmod 644 <path> and ensure the running user owns or can read it.
- If the file is generated earlier in the pipeline, verify that step succeeded before invoking dive.
- Remove the --ci-config flag to fall back to the built-in default CI rules if the file is optional.
Example fix
# before chmod 600 ci-rules.yaml && sudo -u nobody dive image --ci --ci-config ci-rules.yaml # read fails # after chmod 644 ci-rules.yaml && dive image --ci --ci-config ci-rules.yaml
Defensive patterns
Strategy: validation
Validate before calling
// wrapper check before invoking dive
if cfgPath != "" {
if _, err := os.ReadFile(cfgPath); err != nil {
return fmt.Errorf("ci config unreadable, fix perms or drop flag: %w", err)
}
} Prevention
- Generate CI rule files with 0644 permissions in pipelines.
- Verify the prior pipeline step that writes the rules file succeeded.
- Treat a missing/unreadable optional rules file as 'drop --ci-config' rather than fail.
When it happens
Trigger: Passing --ci-config <path> where the file exists but cannot be opened: permission denied (mode 000, wrong owner), a race where the file is deleted between the stat and read, or a symlink pointing to an unreadable target.
Common situations: CI jobs where the rules file is generated by a prior step with restrictive permissions; Docker volume-mounted configs owned by a different UID; TOCTOU deletion in shared workspaces.
Related errors
- failed to unmarshal CI config file %s: %w
- cannot open export file: %w
- cannot export analysis: %w
- directory for JSON export does not exist: %s
- no keybinding configured for '%s'
AI-assisted analysis of wagoodman/dive@d6c691947f (2026-08-15).
Data as JSON: /api/errors/c74d0fc9ddf57cb1.
Report an issue: GitHub.