unslothai/unsloth · error · ValueError

s3_config requires either use_iam_role=True or both access_k

Error message

s3_config requires either use_iam_role=True or both access_key_id and secret_access_key

What it means

Raised by the _check_credentials model_validator on S3Config in the training models. The S3 configuration must be complete in one of two ways: IAM role auth (use_iam_role=True) or a full access-key pair (both access_key_id and secret_access_key). This prevents half-configured credentials that would otherwise fail later at upload time with confusing AWS SDK errors. Truthiness is used, so an empty-string key also triggers the error.

Source

Thrown at studio/backend/models/training.py:75

        alias = "accessKeyId",
        description = "AWS access key ID (optional if using IAM role)",
    )
    secret_access_key: Optional[str] = Field(
        None,
        alias = "secretAccessKey",
        description = "AWS secret access key (optional if using IAM role)",
    )
    use_iam_role: bool = Field(
        False,
        alias = "useIamRole",
        description = "Use IAM role credentials instead of access keys",
    )

    @model_validator(mode = "after")
    def _check_credentials(self) -> "S3Config":
        # Require either IAM role auth or a full key pair so credentials are never half-configured.
        if not self.use_iam_role and not (self.access_key_id and self.secret_access_key):
            raise ValueError(
                "s3_config requires either use_iam_role=True or both "
                "access_key_id and secret_access_key"
            )
        return self


def _parse_lr(v: Any) -> float:
    """Parse learning_rate as a positive float strictly below _MAX_LR_VALUE."""
    if v is None:
        raise ValueError("learning_rate is required")
    if isinstance(v, bool):
        raise ValueError("learning_rate must be a number, not a bool")
    try:
        lr = float(v)
    except (TypeError, ValueError):
        raise ValueError(f"learning_rate must be parseable as float (got {v!r})")
    if not (lr > 0.0):
        raise ValueError(f"learning_rate must be > 0 (got {lr!r}); typical range is 1e-6 .. 1e-3")

View on GitHub (pinned to 203007d190)

Solutions

  1. Supply both access_key_id and secret_access_key in s3_config.
  2. Or set use_iam_role=true when running on an instance/cluster with an attached IAM role.
  3. Verify env-var/secret-manager lookups fail loudly rather than yielding empty strings that get serialized into the request.
  4. Check that the client serializes the camelCase aliases (e.g. useIamRole) correctly so the fields actually reach the server.

Example fix

// before
s3: { access_key_id: process.env.S3_KEY ?? "" }
// after
s3: {
  access_key_id: process.env.S3_KEY!,
  secret_access_key: process.env.S3_SECRET!,
}
// or
s3: { use_iam_role: true }
Defensive patterns

Strategy: validation

Validate before calling

def s3_credentials_complete(cfg: dict) -> bool:
    if cfg.get("use_iam_role") or cfg.get("useIamRole"):
        return True
    return bool(cfg.get("access_key_id")) and bool(cfg.get("secret_access_key"))

Type guard

function s3ConfigOk(cfg: { use_iam_role?: boolean; access_key_id?: string; secret_access_key?: string }): boolean {
  return Boolean(cfg.use_iam_role) || (Boolean(cfg.access_key_id) && Boolean(cfg.secret_access_key));
}

Prevention

When it happens

Trigger: POST a training start/continue request whose s3_config has use_iam_role unset/false and either access_key_id or secret_access_key missing, None, or empty string, e.g. {"s3_config": {"accessKey": "AKIA..."}} with no secret.

Common situations: Storing the access key in an env var that is unset in CI so the field serializes as None; toggling from IAM-role deployment (EKS/k8s) to local development and forgetting to add keys; secret-manager lookups that return empty strings on permission errors; field-name mismatch (access_key_id vs alias accessKey) making one field silently missing.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/bbb77646a72c0ecc. Report an issue: GitHub.